From 50cc70783d86caf500f5214732c642829c4ae88d Mon Sep 17 00:00:00 2001 From: Angelos Chatzimparmpas Date: Wed, 20 Feb 2019 08:35:07 +0100 Subject: [PATCH] added comments, fixed schema investigation --- css/style.css | 6 +- index.html | 10 +- js/data_form_handler.js | 2 +- js/tsne_vis (original).js | 2817 +++++++++++++++++++++++++++++++++++++ js/tsne_vis.js | 2143 ++++++++++++++-------------- 5 files changed, 3933 insertions(+), 1045 deletions(-) create mode 100755 js/tsne_vis (original).js diff --git a/css/style.css b/css/style.css index 1633c4d..de76298 100755 --- a/css/style.css +++ b/css/style.css @@ -149,7 +149,7 @@ cursor: default; /* Legend of the Overview t-SNE canvas */ - div#legend3 { + div#legend2 { height: 11vw; width: 11vw; text-align: left; @@ -182,7 +182,7 @@ text.label { } /* Styling of the ShepardHeatmap */ -svg#legend4 { +svg#legend3 { height: 11vw; width: 11vw; text-align: left; @@ -327,7 +327,7 @@ rect { /* Styling of the main SVG behind canvas */ #SvgAnnotator { position: absolute; - z-index: 3; + z-index: 1; } #toggleAnnotator { diff --git a/index.html b/index.html index f1edd62..e50ad9f 100755 --- a/index.html +++ b/index.html @@ -53,7 +53,7 @@
-
+
@@ -109,7 +109,7 @@
- +
@@ -258,7 +258,7 @@ $('#param-maxiter').bind('input', function () { $('#param-maxiter-value').text($('#param-maxiter').val()); }); // Get the container element - var btnContainer = document.getElementById("commBtn"); + var btnContainer = document.getElementById("commBtn"); //Add a new comment button // Get all buttons with class="btn" inside the container var btns = btnContainer.getElementsByClassName("btn"); @@ -271,7 +271,7 @@ } // Get the container element - var btnContainer2 = document.getElementById("resetAllFilters"); + var btnContainer2 = document.getElementById("resetAllFilters"); //resetAllFilters button // Get all buttons with class="btn" inside the container var btns2 = btnContainer2.getElementsByClassName("btn"); @@ -292,7 +292,7 @@ if ( document.getElementById('cost').hasChildNodes() ) { // Clear legend for the shepardHeatmap - var svgLegend = d3.select("#legend4"); + var svgLegend = d3.select("#legend3"); svgLegend.selectAll("*").remove(); // Clear the SheapHeardmap before refreshing diff --git a/js/data_form_handler.js b/js/data_form_handler.js index 775be1c..a8218d8 100755 --- a/js/data_form_handler.js +++ b/js/data_form_handler.js @@ -3,7 +3,7 @@ function changeDataset(value) { var format = value.split("."); // Get the data set's format. - if (format[value.split(".").length-1] != "csv") { // This is for a new file. + if (format[value.split(".").length-1] != "csv") { // This is a function that handles a new file, which users can upload. d3.select("#data").select("input").remove(); d3.select("#data") .append("input") diff --git a/js/tsne_vis (original).js b/js/tsne_vis (original).js new file mode 100755 index 0000000..f9d1f87 --- /dev/null +++ b/js/tsne_vis (original).js @@ -0,0 +1,2817 @@ +// t-SNE.js object and other global variables + +var toggleValue = false; var k; var points = []; var all_fields; var pointsbeta = []; var KNNEnabled = true; +// These are the dimensions for the square shape of the main panel\ +var dimensions = document.getElementById('modtSNEcanvas').offsetWidth; + +// These are the dimensions for the overview panel +var dim = document.getElementById('tSNEcanvas').offsetWidth; + +var format; var new_file; var opt; var step_counter; var final_dataset; var max_counter; var dists; var dists2d; var all_labels; var runner; var tsne; var count_canvas = 0; var x_position = []; var y_position = []; var x_position2 = []; var y_position2 = []; var cost_each; var beta_all = []; +var points2d = []; var ArrayContainsDataFeatures = []; var ArrayContainsDataFeaturesCleared = []; +var InitialStatePoints = []; + +function getfile(file){ + new_file = file; //uploaded file data +} + +// Parse data +var getData = function() { + // form controls + var value = document.getElementById("param-dataset").value; + + format = document.getElementById("param-dataset").value.split("."); //get the actual format + if (format[value.split(".").length-1] == "csv") { + parseData("./data/"+value); + }else{ + parseData(new_file, init); + } +}; + +function parseData(url) { + Papa.parse(url, { //for csv file! + download: true, + header: true, + dynamicTyping: true, + skipEmptyLines: true, + complete: function(results) { + results.data = results.data.filter(function (el) { + var counter = 0; + for(key in el) { + if(el.hasOwnProperty(key)) { + var value = el[key]; + if (key === 'name'){ + }else{ + if(typeof(value) !== 'number' || value === undefined || key === "Version"){ //add more limitations if needed! + delete el[key]; + }else{ + el[counter] = el[key]; + delete el[key]; + counter = counter + 1; + } + } + } + } + return el; + }); + Papa.parse(url, { //for csv file! + download: true, + header: true, + dynamicTyping: true, + skipEmptyLines: true, + complete: function(data) { + doStuff(data.data); + } + }); + function doStuff(results_all){ + init(results.data, results_all, results.meta.fields); + } + } + }); +} + +function setContinue(){ + d3v3.select("#SvgAnnotator").style("z-index", 2); +} + +var ringNotes = []; +var gAnnotationsAll = []; +var AnnotationsAll = []; +var draggable = []; + +function setReset(){ + d3.selectAll("#SvgAnnotator > *").remove(); + d3.selectAll("#OverviewtSNE > *").remove(); + d3.selectAll("#correlation > *").remove(); + d3.selectAll("#modtSNEcanvas_svg > *").remove(); + d3.selectAll("#kNNBar > *").remove(); + for (var i=0; i < InitialStatePoints.length; i++){ + InitialStatePoints[i].selected = true; + } + redraw(InitialStatePoints); +} + +function setLayerProj(){ + d3.select("#modtSNEcanvas").style("z-index", 2); + d3.select("#modtSNEcanvas_svg").style("z-index", 1); +} + +function setLayerComp(){ + d3.select("#modtSNEcanvas_svg").style("z-index", 3); +} + +function setLayerSche(){ + d3.select("#modtSNEcanvas_svg").style("z-index", 3); + toggleValue = true; +} + + + + +function setAnnotator(){ + + var viewport2 = getViewport(); + var vw2 = viewport2[0]; + var vh2 = viewport2[1]; + var textarea = document.getElementById("comment").value; + + var annotations = [ + { + "cx": 232, + "cy": 123, + "r": 103, + "text": textarea, + "textOffset": [ + 114, + 88 + ] + } + ]; + + var ringNote = d3v3.ringNote() + .draggable(true); + + var svgAnnotator = d3v3.select("#SvgAnnotator") + .attr("width", vw2 * 0.5) + .attr("height", vh2 * 0.888) + .style("z-index", 3); + + + var gAnnotations = svgAnnotator.append("g") + .attr("class", "annotations") + .call(ringNote, annotations); + + // Styling individual annotations based on bound data + gAnnotations.selectAll(".annotation circle") + .classed("shaded", function(d) { return d.shaded; }); + + + ringNotes.push(ringNote); + gAnnotationsAll.push(gAnnotations); + AnnotationsAll.push(annotations); + draggable.push(true); +} + + + // Hide or show the controls + d3.select("#controls") + .on("change", function() { + if(ringNotes[0]){ + for (var i = 0; i < ringNotes.length; i++){ + ringNotes[i].draggable(draggable[i] = !draggable[i]); + gAnnotationsAll[i] + .call(ringNotes[i], AnnotationsAll[i]) + .selectAll(".annotation circle") + .classed("shaded", function(d) { return d.shaded; }); + } + } else{ + alert("Cannot hide the annotators' controls because, currently, there are no annotations into the visual representation.") + } + }); + + // Three.js render loop + function animate() { + requestAnimationFrame(animate); + renderer.render(scene, camera); + } + + var MainCanvas; + var Child; + var renderer; + var fov = 21; + var near = 10; + var far = 7000; + var camera; + var scene; + +// function that executes after data is successfully loaded +function init(data, results_all, fields) { + + MainCanvas = document.getElementById('modtSNEcanvas'); + Child = document.getElementById('modtSNEDiv'); + + // Add canvas + renderer = new THREE.WebGLRenderer({ canvas: MainCanvas}); + renderer.setSize(dimensions, dimensions); + Child.append(renderer.domElement); + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + + // Set up camera and scene + camera = new THREE.PerspectiveCamera( + fov, + dimensions / dimensions, + near, + far + ); + animate(); + step_counter = 0; + max_counter = document.getElementById("param-maxiter-value").value; + opt = {}; + var fields; + fields.push("beta"); + fields.push("cost"); + all_fields = fields; + opt.epsilon = document.getElementById("param-learningrate-value").value; // epsilon is learning rate (10 = default) + opt.perplexity = document.getElementById("param-perplexity-value").value; // roughly how many neighbors each point influences (30 = default) + tsne = new tsnejs.tSNE(opt); + final_dataset = data; + dataFeatures = results_all; + var object; + for (let k = 0; k < dataFeatures.length; k++){ + ArrayContainsDataFeatures.push(Object.values(dataFeatures[k]).concat(k)); + object = []; + Object.values(dataFeatures[k]).forEach(function(dataFeature){ + if(typeof(dataFeature) == "number"){ + object.push(dataFeature); + } + }); + ArrayContainsDataFeaturesCleared.push(object); + } + + $("#datasetDetails").html("Number of Dimensions: " + (ArrayContainsDataFeatures[0].length - 1) + ", Number of Samples: " + final_dataset.length); + dists = computeDistances(data, document.getElementById("param-distance").value, document.getElementById("param-transform").value); + tsne.initDataDist(dists); + all_labels = []; + for(var i = 0; i < data.length; i++) { + if (final_dataset[i]["name"] != "" || final_dataset[i]["name"] != "undefined"){ + all_labels[i] = final_dataset[i]["name"]; + } + else{ + all_labels[i]; + } + } + for(var i = 0; i < final_dataset.length; i++) {final_dataset[i].beta = tsne.beta[i]; beta_all[i] = tsne.beta[i];} + runner = setInterval(step, 0); +} + +// initialize distance matrix +function initDist(data) { + var dist = new Array(data.length); + for(var i = 0; i < data.length; i++) { + dist[i] = new Array(data.length); + } + for(var i = 0; i < data.length; i++) { + for(var j = 0; j < data.length; j++) { + dist[i][j] = 0; + } + } + return dist; +} + +// calculate euclidean distance +function euclideanDist(data) { + dist = initDist(data); + for(var i = 0; i < data.length; i++) { + for(var j = i + 1; j < data.length; j++) { + for(var d in data[0]) { + if(d != "name") { + dist[i][j] += Math.pow(data[i][d] - data[j][d], 2); + } + } + dist[i][j] = Math.sqrt(dist[i][j]); + dist[j][i] = dist[i][j]; + } + } + return dist; +} + +// calculate jaccard dist +function jaccardDist(data) { + dist = initDist(data); + for(var i = 0; i < data.length; i++) { + for(var j = i + 1; j < data.length; j++) { + for(var d in data[0]) { + if(d != "name") { + x = data[i][d]; + y = data[j][d]; + if(x == y) { + dist[i][j] += 1; + } + } + } + dist[j][i] = dist[i][j]; + } + } + return dist; +} + +// normalize distances to prevent numerical issues +function normDist(data, dist) { + var max_dist = 0; + for(var i = 0; i < data.length; i++) { + for(var j = i + 1; j < data.length; j++) { + if(dist[i][j] > max_dist) max_dist = dist[i][j]; + } + } + for(var i = 0; i < data.length; i++) { + for(var j = 0; j < data.length; j++) { + dist[i][j] /= max_dist; + } + } + return dist; +} + +function noTrans(data) { + return data; +} + +// Log transform +function logTrans(data) { + for(var i = 0; i < data.length; i++) { + for(var d in data[0]) { + if(d != "name") { + X = data[i][d]; + data[i][d] = Math.log10(X + 1); + } + } + } + return data; +} + +// asinh transform +function asinhTrans(data) { + for(var i = 0; i < data.length; i++) { + for(var d in data[0]) { + if(d != "name") { + X = data[i][d]; + data[i][d] = Math.log(X + Math.sqrt(X * X + 1)); + } + } + } + return data; +} +// binarize +function binTrans(data) { + for(var i = 0; i < data.length; i++) { + for(var d in data[0]) { + if(d != "name") { + X = data[i][d]; + if(X > 0) data[i][d] = 1; + if(X < 0) data[i][d] = 0; + } + } + } + return data; +} + +function computeDistances(data, distFunc, transFunc) { + dist = eval(distFunc)(eval(transFunc)(data)); + dist = normDist(data, dist); + return dist; +} + +// function that updates embedding +function updateEmbedding() { + var Y = tsne.getSolution(); // here we get the solution from the actual t-sne + var xExt = d3.extent(Y, d => d[0]); + var yExt = d3.extent(Y, d => d[1]); + var maxExt = [Math.min(xExt[0], yExt[0]), Math.max(xExt[1], yExt[1])]; + + var x = d3.scaleLinear() + .domain(maxExt) + .range([10, +dimensions-10]); + + var y = d3.scaleLinear() + .domain(maxExt) + .range([10, +dimensions-10]); + + for(var i = 0; i < final_dataset.length; i++) { + x_position[i] = x(Y[i][0]); + y_position[i] = y(Y[i][1]); + points[i] = {id: i, x: x_position[i], y: y_position[i], beta: final_dataset[i].beta, cost: final_dataset[i].cost, selected: true, DimON: null, starplot: false}; + points2d[i] = {id: i, x: x_position[i], y: y_position[i], selected: true}; + points[i] = extend(points[i], ArrayContainsDataFeaturesCleared[i]); + points[i] = extend(points[i], dataFeatures[i]); + } + InitialStatePoints = points; + function extend(obj, src) { + for (var key in src) { + if (src.hasOwnProperty(key)) obj[key] = src[key]; + } + return obj; + } + if (step_counter == max_counter){ + ShepardHeatMap(); + OverviewtSNE(points); + BetatSNE(points); + } +} + +function ShepardHeatMap () { + var margin = { top: 35, right: 15, bottom: 15, left: 35 }, + dim2 = Math.min(parseInt(d3.select("#sheparheat").style("width")), parseInt(d3.select("#sheparheat").style("height"))) + width = dim2- margin.left - margin.right, + height = dim2 - margin.top - margin.bottom, + buckets = 10, + gridSize = width / buckets, + dim_1 = ["0.0", "0.2", "0.4", "0.6", "0.8", "1.0"], + dim_2 = ["0.0", "0.4", "0.6", "1.0"] + + // Create the svg canvas + var svg = d3.select("#sheparheat") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); +dists2d = computeDistances(points2d, document.getElementById("param-distance").value, document.getElementById("param-transform").value); + +var dist_list2d = []; +var dist_list = []; + for (var j=0; j data[l-1].dim2/10){ + counnum[l] = counnum[l] + 1; + } + }else if (l % 10 == 1){ + temp_loop = data[l].dim1-1; + if (dist_list_all[0][k] < data[l].dim1/10 && dist_list_all[1][k] < data[l].dim2/10 && dist_list_all[0][k] > temp_loop/10){ + counnum[l] = counnum[l] + 1; + } + }else{ + if (dist_list_all[0][k] <= data[l].dim1/10 && dist_list_all[1][k] <= data[l].dim2/10 && dist_list_all[1][k] >= data[l-1].dim2/10 && dist_list_all[0][k] > temp_loop/10){ + counnum[l] = counnum[l] + 1; + } + } + } + counter = counter + counnum[l]; + } + for (var m=0; m" + Math.round(d.value); + }); + + tip(svg.append("g")); + + var dim1Labels = svg.selectAll(".dim1Label") + .data(dim_1) + .enter().append("text") + .text(function (d) { return d; }) + .attr("x", 0) + .attr("y", function (d, i) { return i * gridSize * 2; }) + .style("text-anchor", "end") + .style("font-size", "10px") + .attr("transform", "translate(-6," + gridSize / 4 + ")") + .attr("class","mono"); + + var title = svg.append("text") + .attr("class", "mono") + .attr("x", -(gridSize * 7)) + .attr("y", -26) + .style("font-size", "12px") + .attr("transform", "rotate(-90)") + .attr("class","mono") + .text("Input Distance"); + + + var title = svg.append("text") + .attr("class", "mono") + .attr("x", gridSize * 3 ) + .attr("y", -26) + .style("font-size", "12px") + .text("Output Distance"); + + var dim2Labels = svg.selectAll(".dim2Label") + .data(dim_2) + .enter().append("text") + .text(function(d) { return d; }) + .attr("x", function(d, i) { return i * gridSize * 3.2; }) + .attr("y", 0) + .style("text-anchor", "middle") + .style("font-size", "10px") + .attr("transform", "translate(" + gridSize / 4 + ", -6)") + .attr("class","mono"); + + var heatMap = svg.selectAll(".dim2") + .data(data) + .enter().append("rect") + .attr("x", function(d) { return (d.dim2 - 1) * gridSize; }) + .attr("y", function(d) { return (d.dim1 - 1) * gridSize; }) + .attr("rx", 0.4) + .attr("ry", 0.4) + .attr("class", "dim2 bordered") + .attr("width", gridSize-2) + .attr("height", gridSize-2) + .style("fill", colors[0]) + .attr("class", "square") + .on('mouseover', tip.show) + .on('mouseout', tip.hide); + + + + heatMap.transition() + .style("fill", function(d) { return colorScale(d.value); }); + + heatMap.append("title").text(function(d) { return d.value; }); + + var heatleg = d3.select("#legend4"); + + heatleg.append("g") + .attr("class", "legendLinear") + .attr("transform", "translate(0,10)"); + + var legend = d3.legendColor() + .labelFormat(d3.format(",.0f")) + .cells(9) + .title("Number of Points") + .scale(colorScale); + + heatleg.select(".legendLinear") + .call(legend); + }); +} + +// perform single t-SNE iteration +function step() { + step_counter++; + if(step_counter <= max_counter) { + var cost = tsne.step(); + cost_each = cost[1]; + for(var i = 0; i < final_dataset.length; i++) final_dataset[i].cost = cost_each[i]; + $("#cost").html("Number of Iteration: " + tsne.iter + ", Overall Cost: " + cost[0].toFixed(3)); + } + else { + clearInterval(runner); + } + updateEmbedding(); +} + +function resize(canvas) { + // Lookup the size the browser is displaying the canvas. + var displayWidth = canvas.clientWidth; + var displayHeight = canvas.clientHeight; + + // Check if the canvas is not the same size. + if (canvas.width != displayWidth || + canvas.height != displayHeight) { + + // Make the canvas the same size + canvas.width = displayWidth; + canvas.height = displayHeight; + } +} + +var Arrayx = []; +var Arrayy = []; +var XYDistId = []; +var Arrayxy = []; +var DistanceDrawing1D = []; +var allTransformPoints = []; +var p; +var pFinal = []; +var paths; +var path; +var ArrayLimit = []; +var minimum; +var correlationResults = []; +var ArrayContainsDataFeaturesLimit = []; + +function OverviewtSNE(points){ + if (step_counter == 1){ + d3.select("#OverviewtSNE").select("g").remove(); + d3.select("#correlation").select("g").remove(); + d3.selectAll("#modtSNEcanvas_svg > *").remove(); + Arrayx = []; + Arrayy = []; + XYDistId = []; + Arrayxy = []; + DistanceDrawing1D = []; + allTransformPoints = []; + p; + pFinal = []; + paths; + path; + ArrayLimit = []; + minimum; + correlationResults = []; + ArrayContainsDataFeaturesLimit = []; + } + var canvas = document.getElementById('tSNEcanvas'); + gl = canvas.getContext('experimental-webgl'); + + // If we don't have a GL context, give up now + if (!gl) { + alert('Unable to initialize WebGL. Your browser or machine may not support it.'); + return; + } + if (all_labels[0] == undefined){ + var colorScale = d3.scaleOrdinal(d3.schemeCategory10).domain(["No Category"]).range(["#0000ff"]); + } + else{ + var colorScale = d3.scaleOrdinal(d3.schemeCategory10).domain(all_labels); + } + d3.select("#legend3").select("svg").remove(); + var svg = d3.select("#legend3").append("svg"); + + svg.append("g") + .attr("class", "legendOrdinal") + .attr("transform", "translate(8,5)"); + + var legendOrdinal = d3.legendColor() + .shape("path", d3.legendSize(100)) + .shapePadding(15) + .scale(colorScale); + + svg.select(".legendOrdinal") + .call(legendOrdinal); + + let vertices = []; + let colors = []; + + for (var i=0; i1 to 0->2 + let zeroToTwo = zeroToOne * 2.0; + let zeroToTwo2 = zeroToOne2 * 2.0; + + // convert from 0->2 to -1->+1 (clipspace) + let clipSpace = zeroToTwo - 1.0; + let clipSpace2 = zeroToTwo2 - 1.0; + singleObj = clipSpace; + vertices.push(singleObj); + singleObj = clipSpace2 * -1; + vertices.push(singleObj); + singleObj = 0.0; + vertices.push(singleObj); + } + for (var i=0; i XYDistId[(m * allTransformPoints.length) + j].distance) { + minimum = XYDistId[(m * allTransformPoints.length) + j].distance; + } + } + + for (var l = 0; l < paths.nodes().length ; l++) { + if (XYDistId[(l * allTransformPoints.length) + j].distance == minimum){ + allTransformPoints[j].bucketID = l; + } + } + } + + var arrays = [], size = allTransformPoints.length; + while (XYDistId.length > 0) { + arrays.push(XYDistId.splice(0, size)); + } + + var arraysCleared = []; + for (var j = 0; j < allTransformPoints.length; j++){ + for (var m=0; m < arrays.length; m++) { + if (allTransformPoints[j].bucketID == m){ + arraysCleared.push(arrays[m][j].concat(allTransformPoints[j].bucketID, Arrayxy[m], arrays[m][j].distance, arrays[m][j].id)); + } + } + } + + for (var i=0; i dist) return 1; + return 0; + }); + } + + var arraysConnected = []; + if (paths.nodes().length == 1) { + arraysConnected = arraysSplitted[0]; + } else { + for (var m=0; m < paths.nodes().length - 1; m++) { + arraysConnected = arraysSplitted[m].concat(arraysSplitted[m+1]); + } + } + + var Order = []; + for (var temp = 0; temp < arraysConnected.length; temp++) { + Order.push(arraysConnected[temp][6]); + } + + for (var i = 0; i < points.length; i++){ + points[i].selected = false; + for (var j = 0; j < ArrayLimit.length; j++){ + if (ArrayLimit[j][6] == points[i].id){ + points[i].selected = true; + } + } + } + redraw(points); + + ArrayContainsDataFeatures = mapOrder(ArrayContainsDataFeatures, Order, 5); + + for (var i = 0; i < ArrayContainsDataFeatures.length; i++){ + for (var j = 0; j < arraysConnected.length; j++){ + if (ArrayContainsDataFeatures[i][5] == arraysConnected[j][6]){ + ArrayContainsDataFeaturesLimit.push(ArrayContainsDataFeatures[i]); + } + } + } + + for (var loop = 0; loop < ArrayContainsDataFeaturesLimit.length; loop++) { + ArrayContainsDataFeaturesLimit[loop].push(loop); + } + + for (var k = 0; k < Arrayxy.length - 1 ; k++){ + d3.select('#modtSNEcanvas_svg').append('line') + .attr("x1", Arrayxy[k][0]) + .attr("y1", Arrayxy[k][1]) + .attr("x2", Arrayxy[k+1][0]) + .attr("y2", Arrayxy[k+1][1]) + .style("stroke","black") + .style("stroke-width",2); + } + + var SignStore = []; + const arrayColumn = (arr, n) => arr.map(x => x[n]); + for (var temp = 0; temp < ArrayContainsDataFeaturesLimit[0].length - 2; temp++) { + var tempData = new Array( + arrayColumn(ArrayContainsDataFeaturesLimit, temp), + arrayColumn(ArrayContainsDataFeaturesLimit, ArrayContainsDataFeaturesLimit[0].length - 1) + ); + if (isNaN(pearsonCorrelation(tempData, 0, 1))) { + } else{ + SignStore.push([temp, pearsonCorrelation(tempData, 0, 1)]); + correlationResults.push(["Dimension "+temp, Math.abs(pearsonCorrelation(tempData, 0, 1))]); + } + } + correlationResults = correlationResults.sort( + function(a,b) { + if (a[1] == b[1]) + return a[0] < b[0] ? -1 : 1; + return a[1] < b[1] ? 1 : -1; + } + ); + + for (var j = 0; j < correlationResults.length; j++) { + for (var i = 0; i < SignStore.length; i++) { + if (SignStore[i][1]*(-1) == correlationResults[j][1]) { + correlationResults[j][1] = parseInt(correlationResults[j][1] * 100) * (-1); + correlationResults[j].push(j); + //correlationResults[j][1] = correlationResults[j][1].toFixed(2)*(-1); + } + if (SignStore[i][1] == correlationResults[j][1]) { + correlationResults[j][1] = parseInt(correlationResults[j][1] * 100); + correlationResults[j].push(j); + } + } + } + ///////////////////////////////////////////////////////////// + ///////////////// Set-up SVG and wrappers /////////////////// + ///////////////////////////////////////////////////////////// + + + var mainGroup = svg.append("g") + .attr("class","mainGroupWrapper") + .attr("transform","translate(" + main_margin.left + "," + main_margin.top + ")") + .append("g") //another one for the clip path - due to not wanting to clip the labels + .attr("clip-path", "url(#clip)") + .style("clip-path", "url(#clip)") + .attr("class","mainGroup") + + var miniGroup = svg.append("g") + .attr("class","miniGroup") + .attr("transform","translate(" + (main_margin.left + main_width + main_margin.right + mini_margin.left) + "," + mini_margin.top + ")"); + + var brushGroup = svg.append("g") + .attr("class","brushGroup") + .attr("transform","translate(" + (main_margin.left + main_width + main_margin.right + mini_margin.left) + "," + mini_margin.top + ")"); + + ///////////////////////////////////////////////////////////// + ////////////////////// Initiate scales ////////////////////// + ///////////////////////////////////////////////////////////// + + main_xScale = d3v3.scale.linear().range([0, main_width]); + mini_xScale = d3v3.scale.linear().range([0, mini_width]); + + main_yScale = d3v3.scale.ordinal().rangeBands([0, main_height], 0.4, 0); + mini_yScale = d3v3.scale.ordinal().rangeBands([0, mini_height], 0.4, 0); + //Based on the idea from: http://stackoverflow.com/questions/21485339/d3-brushing-on-grouped-bar-chart + main_yZoom = d3v3.scale.linear() + .range([0, main_height]) + .domain([0, main_height]); + + //Create x axis object + main_xAxis = d3v3.svg.axis() + .scale(main_xScale) + .orient("bottom") + .ticks(8) + .outerTickSize(0); + + //Add group for the x axis + d3v3.select(".mainGroupWrapper").append("g") + .attr("class", "x axis") + .attr("transform", "translate(" + 0 + "," + (main_height + 5) + ")"); + + //Create y axis object + main_yAxis = d3v3.svg.axis() + .scale(main_yScale) + .orient("left") + .tickSize(0) + .outerTickSize(0); + + //Add group for the y axis + mainGroup.append("g") + .attr("class", "y axis") + .attr("transform", "translate(-5,0)"); + + ///////////////////////////////////////////////////////////// + /////////////////////// Update scales /////////////////////// + ///////////////////////////////////////////////////////////// + //Update the scales + main_xScale.domain([-100, 100]); + mini_xScale.domain([-100, 100]); + main_yScale.domain(correlationResults.map(function(d) { return d[0]; })); + mini_yScale.domain(correlationResults.map(function(d) { return d[0]; })); + + //Create the visual part of the y axis + d3v3.select(".mainGroup").select(".y.axis").call(main_yAxis); + d3v3.select(".mainGroupWrapper").select(".x.axis").call(main_xAxis); + + ///////////////////////////////////////////////////////////// + ///////////////////// Label axis scales ///////////////////// + ///////////////////////////////////////////////////////////// + + textScale = d3v3.scale.linear() + .domain([15,50]) + .range([12,6]) + .clamp(true); + + ///////////////////////////////////////////////////////////// + ///////////////////////// Create brush ////////////////////// + ///////////////////////////////////////////////////////////// + + //What should the first extent of the brush become - a bit arbitrary this + var brushExtent = parseInt(Math.max( 1, Math.min( 20, Math.round(correlationResults.length * 0.75) ) )); + + brush = d3v3.svg.brush() + .y(mini_yScale) + .extent([mini_yScale(correlationResults[0][0]), mini_yScale(correlationResults[brushExtent][0])]) + .on("brush", brushmove) + + //Set up the visual part of the brush + gBrush = d3v3.select(".brushGroup").append("g") + .attr("class", "brush") + .call(brush); + + gBrush.selectAll(".resize") + .append("line") + .attr("x2", mini_width); + + gBrush.selectAll(".resize") + .append("path") + .attr("d", d3v3.svg.symbol().type("triangle-up").size(20)) + .attr("transform", function(d,i) { + return i ? "translate(" + (mini_width/2) + "," + 4 + ") rotate(180)" : "translate(" + (mini_width/2) + "," + -4 + ") rotate(0)"; + }); + + gBrush.selectAll("rect") + .attr("width", mini_width); + + //On a click recenter the brush window + gBrush.select(".background") + .on("mousedown.brush", brushcenter) + .on("touchstart.brush", brushcenter); + /////////////////////////////////////////////////////////////////////////// + /////////////////// Create a rainbow gradient - for fun /////////////////// + /////////////////////////////////////////////////////////////////////////// + + defs = svg.append("defs") + + //Create two separate gradients for the main and mini bar - just because it looks fun + createGradient("gradient-main", "60%"); + createGradient("gradient-mini", "13%"); + + //Add the clip path for the main bar chart + defs.append("clipPath") + .attr("id", "clip") + .append("rect") + .attr("x", -main_margin.left) + .attr("width", main_width + main_margin.left) + .attr("height", main_height); + + ///////////////////////////////////////////////////////////// + /////////////// Set-up the mini bar chart /////////////////// + ///////////////////////////////////////////////////////////// + + //The mini brushable bar + //DATA JOIN + var mini_bar = d3v3.select(".miniGroup").selectAll(".bar") + .data(correlationResults, function(d) { return +d[2]; }); + + //UDPATE + mini_bar + .attr("width", function(d) { return Math.abs(mini_xScale(d[1]) - mini_xScale(0)); }) + .attr("y", function(d,i) { return mini_yScale(d[0]); }) + .attr("height", mini_yScale.rangeBand()) + + //ENTER + mini_bar.enter().append("rect") + .attr("class", "bar") + .attr("x", function (d) { return mini_xScale(Math.min(0, d[1])); }) + .attr("width", function(d) { return Math.abs(mini_xScale(d[1]) - mini_xScale(0)); }) + .attr("y", function(d,i) { return mini_yScale(d[0]); }) + .attr("height", mini_yScale.rangeBand()) + .style("fill", "url(#gradient-mini)"); + + //EXIT + mini_bar.exit() + .remove(); + + //Start the brush + //gBrush.call(brush.event); + gBrush.call(brush.event); + } + }); + +} + +//Function runs on a brush move - to update the big bar chart +function updateBarChart() { + + ///////////////////////////////////////////////////////////// + ////////// Update the bars of the main bar chart //////////// + ///////////////////////////////////////////////////////////// + + var bar = d3v3.select(".mainGroup").selectAll(".bar") + .data(correlationResults, function(d) { return +d[2]; }) + //, function(d) { return d.key; }); + + bar + .attr("x", function (d) { return main_xScale(Math.min(0, d[1])); }) + .attr("width", function(d) { return Math.abs(main_xScale(d[1]) - main_xScale(0)); }) + .attr("y", function(d,i) { return main_yScale(d[0]); }) + .attr("height", main_yScale.rangeBand()); + + //ENTER + bar.enter().append("rect") + .attr("class", "bar") + .style("fill", "url(#gradient-main)") + .attr("x", function (d) { return main_xScale(Math.min(0, d[1])); }) + .attr("width", function(d) { return Math.abs(main_xScale(d[1]) - main_xScale(0)); }) + .attr("y", function(d,i) { return main_yScale(d[0]); }) + .attr("height", main_yScale.rangeBand()) + .on("mouseover", () => { + svg.select('.tooltip').style('display', 'none'); + }) + .on("mouseout", function(d){ + points.forEach(function (p) { + p.DimON = null + }) + BetatSNE(points); + svg.select('.tooltip').style('display', 'none'); + }) + .on("mousemove", function(d) { + points.forEach(function (p) { + if (p.selected == true) { + p.DimON = d[0]; + } + }) + BetatSNE(points); + }); + + //EXIT + bar.exit() + .remove(); +}//update + +///////////////////////////////////////////////////////////// +////////////////////// Brush functions ////////////////////// +///////////////////////////////////////////////////////////// + +//First function that runs on a brush move +function brushmove() { + + var extent = brush.extent(); + + //Reset the part that is visible on the big chart + var originalRange = main_yZoom.range(); + main_yZoom.domain( extent ); + + //Update the domain of the x & y scale of the big bar chart + main_yScale.domain(correlationResults.map(function(d) { return d[0]; })); + main_yScale.rangeBands( [ main_yZoom(originalRange[0]), main_yZoom(originalRange[1]) ], 0.4, 0); + + //Update the y axis of the big chart + d3v3.select(".mainGroup") + .select(".y.axis") + .call(main_yAxis); + + //Which bars are still "selected" + var selected = mini_yScale.domain() + .filter(function(d) { return (extent[0] - mini_yScale.rangeBand () + 1e-2 <= mini_yScale(d)) && (mini_yScale(d) <= extent[1] - 1e-2); }); + + //Update the colors of the mini chart - Make everything outside the brush grey + d3.select(".miniGroup").selectAll(".bar") + .style("fill", function(d, i) { return selected.indexOf(d[0]) > -1 ? "url(#gradient-mini)" : "#e0e0e0"; }); + + //Update the label size + d3v3.selectAll(".y.axis text") + .style("font-size", textScale(selected.length)); + + //Update the big bar chart + updateBarChart(); + +}//brushmove + +///////////////////////////////////////////////////////////// +////////////////////// Click functions ////////////////////// +///////////////////////////////////////////////////////////// + + //Based on http://bl.ocks.org/mbostock/6498000 + //What to do when the user clicks on another location along the brushable bar chart + function brushcenter() { + var target = d3v3.event.target, + extent = brush.extent(), + size = extent[1] - extent[0], + range = mini_yScale.range(), + y0 = d3v3.min(range) + size / 2, + y1 = d3.max(range) + mini_yScale.rangeBand() - size / 2, + center = Math.max( y0, Math.min( y1, d3.mouse(target)[1] ) ); + + d3v3.event.stopPropagation(); + + gBrush + .call(brush.extent([center - size / 2, center + size / 2])) + .call(brush.event); + + }//brushcenter + + function scroll() { + + //Mouse scroll on the mini chart + var extent = brush.extent(), + size = extent[1] - extent[0], + range = mini_yScale.range(), + y0 = d3v3.min(range), + y1 = d3v3.max(range) + mini_yScale.rangeBand(), + dy = d3v3.event.deltaY, + topSection; + + if ( extent[0] - dy < y0 ) { topSection = y0; } + else if ( extent[1] - dy > y1 ) { topSection = y1 - size; } + else { topSection = extent[0] - dy; } + + //Make sure the page doesn't scroll as well + d3v3.event.stopPropagation(); + d3v3.event.preventDefault(); + + gBrush + .call(brush.extent([ topSection, topSection + size ])) + .call(brush.event); + + }//scroll + +///////////////////////////////////////////////////////////// +///////////////////// Helper functions ////////////////////// +///////////////////////////////////////////////////////////// + +//Create a gradient +function createGradient(idName, endPerc) { + + var colorsBarChart = ['#7f3b08','#b35806','#e08214','#fdb863','#fee0b6','#d8daeb','#b2abd2','#8073ac','#542788','#2d004b'] + + colorsBarChart.reverse(); + + defs.append("linearGradient") + .attr("id", idName) + .attr("gradientUnits", "userSpaceOnUse") + .attr("x1", "0%").attr("y1", "0%") + .attr("x2", endPerc).attr("y2", "0%") + .selectAll("stop") + .data(colorsBarChart) + .enter().append("stop") + .attr("offset", function(d,i) { return i/(colorsBarChart.length-1); }) + .attr("stop-color", function(d) { return d; }); +}//createGradient + +function mapOrder(array, order, key) { + + array.sort( function (a, b) { + var A = a[key], B = b[key]; + if (order.indexOf(A) > order.indexOf(B)) { + return 1; + } else { + return -1; + } + + }); + + return array; +}; + + /** + * Calculate the person correlation score between two items in a dataset. + * + * @param {object} prefs The dataset containing data about both items that + * are being compared. + * @param {string} p1 Item one for comparison. + * @param {string} p2 Item two for comparison. + * @return {float} The pearson correlation score. + */ + function pearsonCorrelation(prefs, p1, p2) { + var si = []; + + for (var key in prefs[p1]) { + if (prefs[p2][key]) si.push(key); + } + + var n = si.length; + + if (n == 0) return 0; + + var sum1 = 0; + for (var i = 0; i < si.length; i++) sum1 += prefs[p1][si[i]]; + + var sum2 = 0; + for (var i = 0; i < si.length; i++) sum2 += prefs[p2][si[i]]; + + var sum1Sq = 0; + for (var i = 0; i < si.length; i++) { + sum1Sq += Math.pow(prefs[p1][si[i]], 2); + } + + var sum2Sq = 0; + for (var i = 0; i < si.length; i++) { + sum2Sq += Math.pow(prefs[p2][si[i]], 2); + } + + var pSum = 0; + for (var i = 0; i < si.length; i++) { + pSum += prefs[p1][si[i]] * prefs[p2][si[i]]; + } + + var num = pSum - (sum1 * sum2 / n); + var den = Math.sqrt((sum1Sq - Math.pow(sum1, 2) / n) * + (sum2Sq - Math.pow(sum2, 2) / n)); + + if (den == 0) return 0; + + return num / den; + } + + function closestPoint(pathNode, point) { + var pathLength = pathNode.getTotalLength(), + precision = 8, + best, + bestLength, + bestDistance = Infinity; + + // linear scan for coarse approximation + for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) { + if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) { + best = scan, bestLength = scanLength, bestDistance = scanDistance; + } + } + + // binary search for precise estimate + precision /= 2; + while (precision > 0.5) { + var before, + after, + beforeLength, + afterLength, + beforeDistance, + afterDistance; + if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) { + best = before, bestLength = beforeLength, bestDistance = beforeDistance; + } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) { + best = after, bestLength = afterLength, bestDistance = afterDistance; + } else { + precision /= 2; + } + } + + best = [best.x, best.y]; + best.distance = Math.sqrt(bestDistance); + best.id = point[2]; + return best; + + function distance2(p) { + var dx = p.x - point[0], + dy = p.y - point[1]; + return dx * dx + dy * dy; + } + + + +// Points are represented as objects with x and y attributes. + + + + /* var svg = d3.select('#modtSNEcanvas_svgClick').append('svg') + .attr('width', width) + .attr('height', height) + .on('mousemove', function() { + x = d3.event.pageX; + y = d3.event.pageY; + console.log( d3.event.pageX, d3.event.pageY ) // log the mouse x,y position + }); + + + var interactionSvgClick = d3.select("#modtSNEcanvas_svgClick").append("circle") + .attr("transform", "translate(" + x + "," + y + ")") + .attr("r", "3") + .attr("class", "dot") + .style('position', 'absolute') + .style("cursor", "pointer");*/ + //.call(drag); +} +/* +// Define drag behavior +var drag = d3.drag() + .on("drag", dragmove); + +function dragmove(d) { + var x = d3.event.x; + var y = d3.event.y; + d3.select(this).attr("transform", "translate(" + x + "," + y + ")"); +} +*/ + +function abbreviateNumber(value) { + var newValue = value; + if (value >= 1000) { + var suffixes = ["", "k", "m", "b","t"]; + var suffixNum = Math.floor( (""+value).length/3 ); + var shortValue = ''; + for (var precision = 2; precision >= 1; precision--) { + shortValue = parseFloat( (suffixNum != 0 ? (value / Math.pow(1000,suffixNum) ) : value).toPrecision(precision)); + var dotLessShortValue = (shortValue + '').replace(/[^a-zA-Z 0-9]+/g,''); + if (dotLessShortValue.length <= 2) { break; } + } + if (shortValue % 1 != 0) shortNum = shortValue.toFixed(1); + newValue = shortValue+suffixes[suffixNum]; + } + return newValue; +} + +function clearThree(obj){ + while(obj.children.length > 0){ + clearThree(obj.children[0]) + obj.remove(obj.children[0]); + } + if(obj.geometry) obj.geometry.dispose() + if(obj.material) obj.material.dispose() + if(obj.texture) obj.texture.dispose() +} + + +var viewport3 = getViewport(); +var vw3 = viewport3[0] * 0.2; +var margin = {top: 50, right: 100, bottom: 80, left: 150}, +width = Math.min(vw3, window.innerWidth - 10) - margin.left - margin.right, +height = Math.min(width, window.innerHeight - margin.top - margin.bottom - 20); + + var wrapData = []; + + ////////////////////////////////////////////////////////////// + //////////////////// Draw the Chart ////////////////////////// + ////////////////////////////////////////////////////////////// + + + var radarChartOptions = { + w: width, + h: height, + margin: margin + }; + var colors; + var IDS = []; + //Call function to draw the Radar chart + RadarChart("#starPlot", wrapData, colors, IDS, radarChartOptions); + +function BetatSNE(points){ + + selectedPoints = []; + var findNearestTable = []; + for (let m=0; m0; k--){ + + findNearest = 0; + var indexOrderSliced = []; + var indexOrderSliced2d = []; + var count1 = new Array(selectedPoints.length).fill(0); + var count2 = new Array(selectedPoints.length).fill(0); + counter1 = 0; + counter2 = 0; + + for (var i=0; i -1) { + indices[i].splice(index, 1); + } + // sorting the mapped array containing the reduced values + indices[i].sort(function(a, b) { + if (a[1] > b[1]) { + return 1; + } + if (a[1] < b[1]) { + return -1; + } + return 0; + }); + + indexOrder[i] = indices[i].map(function(value) { return value[0]; }); + + // temporary array holds objects with position and sort-value + indices2d[i] = dists2d[i].map(function(el, i) { + return [ i, el ]; + }) + var index2d = indices2d[i].indexOf(selectedPoints[i].id); + if (index2d > -1) { + indices2d[i].splice(index2d, 1); + } + + // sorting the mapped array containing the reduced values + indices2d[i].sort(function(a, b) { + if (a[1] > b[1]) { + return 1; + } + if (a[1] < b[1]) { + return -1; + } + return 0; + }); + indexOrder2d[i] = indices2d[i].map(function(value) { return value[0]; }); + } + indexOrderSliced[i] = indexOrder[i].slice(0,k); + indexOrderSliced2d[i] = indexOrder2d[i].slice(0,k); + + for (var m=0; m < indexOrderSliced2d[i].length; m++){ + if (indexOrderSliced[i].includes(indexOrderSliced2d[i][m])){ + count1[i] = count1[i] + 1; + temp[i] = temp[i] + 1; + } + if(indexOrderSliced[i][m] == indexOrderSliced2d[i][m]){ + count2[i] = count2[i] + 1; + temp2[i] = temp2[i] + 1; + + } + } + + if (count1[i] != 0){ + counter1 = (count1[i] / temp[i]) + counter1; + } + if (count2[i] != 0){ + counter2 = (count2[i] / temp2[i]) + counter2; + } + + } + + sumUnion = counter1 / selectedPoints.length; + sumIntersection = counter2 / selectedPoints.length; + if (sumUnion == 0){ + findNearest = 0; + } else{ + findNearest = sumIntersection / sumUnion; + } + + if (isNaN(findNearest)){ + findNearest = 0; + } + findNearestTable.push(findNearest * vh * 2); + } + findNearestTable.reverse(); + + var barPadding = 5; + d3v3.select("#knnBarChart").selectAll("rect").remove(); + + var svg2 = d3v3.select('#knnBarChart') + .attr("class", "bar-chart"); + + + var barWidth = (vw / findNearestTable.length); + + var knnBarChartSVG = svg2.selectAll("rect") + .data(findNearestTable) + .enter() + .append("rect") + .attr("y", function(d) { + return Math.round(vh*2 - d) + }) + .attr("height", function(d) { + return d; + }) + .attr("width", barWidth - barPadding) + .attr("transform", function (d, i) { + var translate = [barWidth * i, 0]; + return "translate("+ translate +")"; + }); + } + + + + + d3.select("#starPlot").selectAll('g').remove(); + + if(selectedPoints.length <= 10){ + + var FeatureWise = []; + + for (var j=0; j { + dimensions = window.innerWidth; + viz_width = dimensions; + dimensions = window.innerHeight; + + renderer.setSize(dimensions, dimensions); + camera.aspect = dimensions / dimensions; + camera.updateProjectionMatrix(); +}) + +let zoom = d3.zoom() + .scaleExtent([getScaleFromZ(far), getScaleFromZ(near)]) + .on('zoom', () => { + let d3_transform = d3.event.transform; + zoomHandler(d3_transform); + }); + +view = d3.select(renderer.domElement); +function setUpZoom() { + view.call(zoom); + let initial_scale = getScaleFromZ(far); + var initial_transform = d3.zoomIdentity.translate(viz_width/2, dimensions/2).scale(initial_scale); + zoom.transform(view, initial_transform); + camera.position.set(0, 0, far); +} +setUpZoom(); + +var circle_sprite= new THREE.TextureLoader().load( + "./textures/circle-sprite.png" +) + +let pointsGeometry = new THREE.Geometry(); + +clearThree(scene); + +// Increase/reduce size factor selected by the user +var limitdist = document.getElementById("param-lim-value").value; +limitdist = parseFloat(limitdist).toFixed(1); + +let pointsMaterial = []; +let factorPlusSize = []; +for (var i=0; i { + let [mouseX, mouseY] = d3.mouse(view.node()); + let mouse_position = [mouseX, mouseY]; +checkIntersects(mouse_position); +}); + +function mouseToThree(mouseX, mouseY) { + return new THREE.Vector3( + mouseX / viz_width * 2 - 1, + -(mouseY / dimensions) * 2 + 1, + 1 + ); +} +function checkIntersects(mouse_position) { + let mouse_vector = mouseToThree(...mouse_position); + raycaster.setFromCamera(mouse_vector, camera); + let intersects = raycaster.intersectObject(particles); + if (intersects[0]) { + let sorted_intersects = sortIntersectsByDistanceToRay(intersects); + let intersect = sorted_intersects[0]; + let index = intersect.index; + let datum = points[index]; + highlightPoint(datum); + showTooltip(mouse_position, datum); + } else { + removeHighlights(); + hideTooltip(); + } +} + +function sortIntersectsByDistanceToRay(intersects) { + return _.sortBy(intersects, "distanceToRay"); +} + +hoverContainer = new THREE.Object3D() +scene.add(hoverContainer); + +function highlightPoint(datum) { + removeHighlights(); + + let geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3( + (((datum.x/dimensions)*2) - 1)*dimensions, + (((datum.y/dimensions)*2) - 1)*dimensions*-1, + 0 + ) + ); + + if (all_labels[0] == undefined){ + var colorScaleCat = d3.scaleOrdinal(d3.schemeCategory10).domain(["No Category"]).range(["#0000ff"]); + } + else{ + var colorScaleCat = d3.scaleOrdinal(d3.schemeCategory10).domain(all_labels); + } + + geometry.colors = [ new THREE.Color(colorScaleCat(datum.name)) ]; + + let material = new THREE.PointsMaterial({ + size: 26, + sizeAttenuation: false, + vertexColors: THREE.VertexColors, + map: circle_sprite, + transparent: true + }); + + let point = new THREE.Points(geometry, material); + hoverContainer.add(point); +} + +function removeHighlights() { + hoverContainer.remove(...hoverContainer.children); +} + +view.on("mouseleave", () => { + removeHighlights() +}); + +// Initial tooltip state +let tooltip_state = { display: "none" } +let tooltip_dimensions; +let tooltip_template = document.createRange().createContextualFragment(``); +document.body.append(tooltip_template); + +let $tooltip = document.querySelector('#tooltip'); +let $point_tip = document.querySelector('#point_tip'); +let $group_tip = document.querySelector('#group_tip'); + +function updateTooltip() { + if (all_labels[0] == undefined){ + var colorScaleCat = d3.scaleOrdinal(d3.schemeCategory10).domain(["No Category"]).range(["#0000ff"]); + } + else{ + var colorScaleCat = d3.scaleOrdinal(d3.schemeCategory10).domain(all_labels); + } + $tooltip.style.display = tooltip_state.display; + $tooltip.style.left = tooltip_state.left + 'px'; + $tooltip.style.top = tooltip_state.top + 'px'; + $point_tip.innerText = tooltip_state.name; + $point_tip.style.background = colorScaleCat(tooltip_state.color); + $group_tip.innerText = `Data set's features: ${tooltip_dimensions}`; +} + +function showTooltip(mouse_position, datum) { + let tooltip_width = 240; + let x_offset = tooltip_width + tooltip_width; + let y_offset = 30; + tooltip_state.display = "block"; + tooltip_state.left = mouse_position[0] + x_offset; + tooltip_state.top = mouse_position[1] + y_offset; + if (all_labels[0] == undefined){ + tooltip_state.name = datum.id; + tooltip_state.color = datum.id; + } else{ + tooltip_state.name = datum.name + " (" + datum.id + ")"; + tooltip_state.color = datum.name; + } + tooltip_dimensions = []; + for (var i=0; i < ArrayContainsDataFeaturesCleared.length; i++){ + if (datum.id == i){ + for (var j=0; j < ArrayContainsDataFeaturesCleared[i].length; j++){ + tooltip_dimensions.push(ArrayContainsDataFeaturesCleared[i][j]); + } + } + } + updateTooltip(); +} + +function hideTooltip() { + tooltip_state.display = "none"; + updateTooltip(); +} + + +} + + function getViewport() { + + var viewPortWidth; + var viewPortHeight; + + // the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight + if (typeof window.innerWidth != 'undefined') { + viewPortWidth = window.innerWidth, + viewPortHeight = window.innerHeight + } + + // IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document) + else if (typeof document.documentElement != 'undefined' + && typeof document.documentElement.clientWidth != + 'undefined' && document.documentElement.clientWidth != 0) { + viewPortWidth = document.documentElement.clientWidth, + viewPortHeight = document.documentElement.clientHeight + } + + // older versions of IE + else { + viewPortWidth = document.getElementsByTagName('body')[0].clientWidth, + viewPortHeight = document.getElementsByTagName('body')[0].clientHeight + } + return [viewPortWidth, viewPortHeight]; + } + + + + /* + var canvas = document.getElementById('modtSNEcanvas'); + var gl = canvas.getContext('webgl'); + // If we don't have a GL context, give up now + + if (!gl) { + alert('Unable to initialize WebGL. Your browser or machine may not support it.'); + return; + } + + var ColSizeSelector = document.getElementById("param-neighborHood").value; + + if (ColSizeSelector == "color") { + var max = (d3.max(final_dataset,function(d){ return d.beta; })); + var min = (d3.min(final_dataset,function(d){ return d.beta; })); + // colors + var colorbrewer = ["#ffffcc","#ffeda0","#fed976","#feb24c","#fd8d3c","#fc4e2a","#e31a1c","#bd0026","#800026"]; + var calcStep = (max-min)/7; + var colorScale = d3.scaleLinear() + .domain(d3.range(0, max+calcStep, calcStep)) + .range(colorbrewer); + + var maxSize1 = (d3.max(final_dataset,function(d){ return d.cost; })); + var minSize1 = (d3.min(final_dataset,function(d){ return d.cost; })); + + var rscale1 = d3.scaleLinear() + .domain([minSize1, maxSize1]) + .range([5,10]); + + var colorScale = d3.scaleLinear() + .domain(d3.range(0, max+calcStep, calcStep)) + .range(colorbrewer); + + points = points.sort(function(a, b) { + return a.beta - b.beta; + }) + var labels_beta = []; + var abbr_labels_beta = []; + labels_beta = d3.range(0, max+calcStep, calcStep); + for (var i=0; i<9; i++){ + labels_beta[i] = parseInt(labels_beta[i]); + abbr_labels_beta[i] = abbreviateNumber(labels_beta[i]); + } + var svg = d3.select("#legend1"); + + svg.append("g") + .attr("class", "legendLinear") + .attr("transform", "translate(10,15)"); + + var legend = d3.legendColor() + .labelFormat(d3.format(",.0f")) + .cells(9) + .labels([abbr_labels_beta[0],abbr_labels_beta[1],abbr_labels_beta[2],abbr_labels_beta[3],abbr_labels_beta[4],abbr_labels_beta[5],abbr_labels_beta[6],abbr_labels_beta[7],abbr_labels_beta[8]]) + .title("1 / sigma") + .scale(colorScale); + + svg.select(".legendLinear") + .call(legend); + } else { + var max = (d3.max(final_dataset,function(d){ return d.cost; })); + var min = (d3.min(final_dataset,function(d){ return d.cost; })); + + var maxSize2 = (d3.max(final_dataset,function(d){ return d.beta; })); + var minSize2 = (d3.min(final_dataset,function(d){ return d.beta; })); + + var rscale2 = d3.scaleLinear() + .domain([minSize2, maxSize2]) + .range([5,10]); + + var colorbrewer = ["#ffffe5","#f7fcb9","#d9f0a3","#addd8e","#78c679","#41ab5d","#238443","#006837","#004529"]; + var calcStep = (max-min)/9; + var colorScale = d3.scaleLinear() + .domain(d3.range(min, max, calcStep)) + .range(colorbrewer); + + points = points.sort(function(a, b) { + return a.cost - b.cost; + }) + + var labels_cost = []; + var abbr_labels_cost = []; + labels_cost = d3.range(min, max, calcStep); + for (var i=0; i<9; i++){ + labels_cost[i] = labels_cost[i].toFixed(5); + abbr_labels_cost[i] = abbreviateNumber(labels_cost[i]); + } + + var svg = d3.select("#legend1"); + + svg.append("g") + .attr("class", "legendLinear") + .attr("transform", "translate(10,15)"); + + var legend = d3.legendColor() + .labelFormat(d3.format(",.5f")) + .cells(9) + .labels([abbr_labels_cost[0],abbr_labels_cost[1],abbr_labels_cost[2],abbr_labels_cost[3],abbr_labels_cost[4],abbr_labels_cost[5],abbr_labels_cost[6],abbr_labels_cost[7],abbr_labels_cost[8]]) + .title("KLD(P||Q)") + .scale(colorScale); + + svg.select(".legendLinear") + .call(legend); + } + + let vertices = []; + let colors = []; + let sizes = new Float32Array(points.length); + let tempSort = -1; + + for (var i=0; i1 to 0->2 + let zeroToTwo = zeroToOne * 2.0; + let zeroToTwo2 = zeroToOne2 * 2.0; + + // convert from 0->2 to -1->+1 (clipspace) + let clipSpace = zeroToTwo - 1.0; + let clipSpace2 = zeroToTwo2 - 1.0; + singleObj = clipSpace; + vertices.push(singleObj); + singleObj = clipSpace2 * -1; + vertices.push(singleObj); + singleObj = 0.0; + vertices.push(singleObj); + } + + for (var i=0; i *").remove(); - d3.selectAll("#SvgAnnotator > *").remove(); - d3.select("#data").select("input").remove(); // Remove the selection field. Arrayx = []; Arrayy = []; XYDistId = []; Arrayxy = []; DistanceDrawing1D = []; allTransformPoints = []; - p; pFinal = []; - paths; - path; ArrayLimit = []; - minimum; correlationResults = []; ArrayContainsDataFeaturesLimit = []; prevRightClick = false; - for (var i=0; i < InitialStatePoints.length; i++){ - InitialStatePoints[i].selected = true; - InitialStatePoints[i].starplot = false; - } - redraw(InitialStatePoints); - - d3.selectAll("#correlation > *").remove(); - d3.selectAll("#modtSNEcanvas_svg > *").remove(); - d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); - d3.selectAll("#SvgAnnotator > *").remove(); - d3.selectAll("#sheparheat > *").remove(); - d3.selectAll("#knnBarChart > *").remove(); + // Remove the previously drawn scene by Three.js var oldcanvOver = document.getElementById('tSNEcanvas'); var contxOver = oldcanvOver.getContext('experimental-webgl'); contxOver.clear(contxOver.COLOR_BUFFER_BIT); @@ -70,35 +63,34 @@ function FactoryReset(){ scene = new THREE.Scene(); scene.background = new THREE.Color(0xffffff); - d3.selectAll("#legend1 > *").remove(); - d3.selectAll("#legend3 > *").remove(); - d3.selectAll("#legend4 > *").remove(); - + // ReEnable lasso interaction lassoEnable(); - Arrayx = []; - Arrayy = []; - XYDistId = []; - Arrayxy = []; - DistanceDrawing1D = []; - allTransformPoints = []; - p; - pFinal = []; - paths; - path; - ArrayLimit = []; - minimum; - correlationResults = []; - ArrayContainsDataFeaturesLimit = []; - + // Set the default data set into iris.csv and return the main execution button into the initial state document.getElementById("param-dataset").value = "iris.csv"; document.getElementById('file-input').value = ""; document.getElementById("ExecuteBut").innerHTML = "Execute new t-SNE analysis"; + // Remove every d3 svg that was already drawn on the screen + d3.selectAll("#correlation > *").remove(); + d3.selectAll("#modtSNEcanvas_svg > *").remove(); + d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); + d3.selectAll("#SvgAnnotator > *").remove(); + d3.selectAll("#sheparheat > *").remove(); + d3.selectAll("#knnBarChart > *").remove(); + d3.select("#data").select("input").remove(); // Remove the selection field. + + // Remove all the legends + d3.selectAll("#legend1 > *").remove(); + d3.selectAll("#legend2 > *").remove(); + d3.selectAll("#legend3 > *").remove(); + + // Remove the extra information such as cost, number of iterations, number of dimensions, and number of samples $("#cost").html(""); $("#datasetDetails").html(""); $("#kNNDetails").html(""); + // Set all parameters into their default values document.getElementById("param-perplexity-value").value = 30; document.getElementById("param-learningrate-value").value = 10; document.getElementById("param-maxiter-value").value = 500; @@ -130,76 +122,91 @@ function fetchVal(callback) { fr.onload = function (e) { lines = e.target.result; callback(lines); -}; + }; fr.readAsText(file); } -// Parse the data set +// Parse the analysis folder if requested or the csv file if we run a new execution. var getData = function() { - let format; - let value; + let format; + let value; - if (typeof window.FileReader !== 'function') { - alert("The file API isn't supported on this browser yet."); - } - - input = document.getElementById("file-input"); - if (!input) { - alert("Um, couldn't find the fileinput element."); - } else if (!input.files) { - alert("This browser doesn't seem to support the `files` property of file inputs."); - } else if (!input.files[0]) { - value = document.getElementById("param-dataset").value; // get the value of the data set - format = document.getElementById("param-dataset").value.split("."); //get the format - - if (format[value.split(".").length-1] == "csv") { - parseData("./data/"+value); - }else{ - parseData(new_file, init); - } - } - else { - fetchVal(function(lines){ - AnalaysisResults = JSON.parse(lines); - length = (AnalaysisResults.length - 7) / 2; - ParametersSet = AnalaysisResults.slice(length*2+1, length*2+7); - value = document.getElementById("param-dataset").value = ParametersSet[0]; - format = document.getElementById("param-dataset").value.split("."); //get the actual format - if (format[value.split(".").length-1] == "csv") { - parseData("./data/"+value); - }else{ - parseData(new_file, init); + if (typeof window.FileReader !== 'function') { + alert("The file API is not supported on this browser yet."); + } + // Check if the input already exists, which means if we loaded a previous analysis + input = document.getElementById("file-input"); + if (!input) { + alert("Could not find the file input element."); + } else if (!input.files) { + alert("This browser does not seem to support the `files` property of file inputs."); + } else if (!input.files[0]) { + + value = document.getElementById("param-dataset").value; // Get the value of the data set + format = value.split("."); //Get the format (e.g., [iris, csv]) + + if (format[value.split(".").length-1] == "csv") { // Parse the predefined files + parseData("./data/"+value); + } else{ + parseData(new_file, init); // Parse new files + } + + } else { + fetchVal(function(lines){ + // Load an analysis and parse the previous points and parameters information. + AnalaysisResults = JSON.parse(lines); + length = (AnalaysisResults.length - 7) / 2; + ParametersSet = AnalaysisResults.slice(length*2+1, length*2+7); + + value = document.getElementById("param-dataset").value = ParametersSet[0]; + format = value.split("."); //Get the actual format + if (format[value.split(".").length-1] == "csv") { + // Check if the file is in the right folder, i.e., ./data/{file} + $.ajax({ + type: 'HEAD', + url: './data/'+value, + complete: function (xhr){ + if (xhr.status == 404){ + alert(xhr.statusText); // Not found + alert("Please, place your new data set into the ./data folder of the implementation."); + } } - - }); + }); + parseData("./data/"+value); } -}; + }); + } +} + +// Parse the data set with the use of PapaParse. function parseData(url) { - Papa.parse(url, { //for csv file! - download: true, - header: true, - dynamicTyping: true, - skipEmptyLines: true, - complete: function(results) { - results.data = results.data.filter(function (el) { - var counter = 0; - for(key in el) { - if(el.hasOwnProperty(key)) { - var value = el[key]; - if(typeof(value) !== 'number' || value === undefined || key === "Version"){ //add more limitations if needed! - delete el[key]; - }else{ - el[counter] = el[key]; - delete el[key]; - counter = counter + 1; - } - } - } - return el; + + Papa.parse(url, { + download: true, + header: true, + dynamicTyping: true, + skipEmptyLines: true, + complete: function(results) { + results.data = results.data.filter(function (el) { + var counter = 0; + for(key in el) { + if(el.hasOwnProperty(key)) { + var value = el[key]; + if(typeof(value) !== 'number' || value === undefined || key === "Version"){ // We can add more limitations here if needed! We delete everything that we do not want. + delete el[key]; + }else{ + el[counter] = el[key]; + delete el[key]; + counter = counter + 1; + } + } + } + return el; }); - Papa.parse(url, { //for csv file! + + Papa.parse(url, { download: true, header: true, dynamicTyping: true, @@ -209,98 +216,156 @@ function parseData(url) { } }); function doStuff(results_all){ - init(results.data, results_all, results.meta.fields); + // results_all variable is all the columns multiplied by all the rows. + // results.data variable is all the columns except strings, undefined values, or "Version" plus beta and cost values." + // results.meta.fields variable is all the features (columns) plus beta and cost strings. + init(results.data, results_all, results.meta.fields); // Call the init() function that starts everything! } } }); + } -function setContinue(){ +function setContinue(){ // This function allows the continuation of the analysis because it decreases the layer value of the annotator. d3v3.select("#SvgAnnotator").style("z-index", 1); } -var ringNotes = []; -var gAnnotationsAll = []; -var AnnotationsAll = []; -var draggable = []; +function setReset(){ // Reset only the filters which were applied into the data points. -function setReset(){ + // Clear d3 SVGs d3.selectAll("#correlation > *").remove(); d3.selectAll("#modtSNEcanvas_svg > *").remove(); - lassoEnable(); - flagForSchema = false; d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); d3.selectAll("#SvgAnnotator > *").remove(); + // Enable lasso interaction + lassoEnable(); + // Disable Schema Investigation + flagForSchema = false; + // Empty all the arrays that are related to Schema Investigation Arrayx = []; Arrayy = []; XYDistId = []; Arrayxy = []; DistanceDrawing1D = []; allTransformPoints = []; - p; pFinal = []; - paths; - path; ArrayLimit = []; - minimum; correlationResults = []; ArrayContainsDataFeaturesLimit = []; prevRightClick = false; + + // Reset the points into their initial state for (var i=0; i < InitialStatePoints.length; i++){ InitialStatePoints[i].selected = true; InitialStatePoints[i].starplot = false; } redraw(InitialStatePoints); + } function setReInitialize(){ + + // Change between color-encoding and size-encoding mapped to 1/sigma and KLD. if (document.getElementById('selectionLabel').innerHTML == 'Size'){ document.getElementById('selectionLabel').innerHTML = 'Color'; } else{ document.getElementById('selectionLabel').innerHTML = 'Size'; } + // Clear d3 SVGs + d3.selectAll("#correlation > *").remove(); + d3.selectAll("#modtSNEcanvas_svg > *").remove(); + d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); + d3.selectAll("#SvgAnnotator > *").remove(); + + // Clear d3 SVGs + d3.selectAll("#correlation > *").remove(); + d3.selectAll("#modtSNEcanvas_svg > *").remove(); + d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); + d3.selectAll("#SvgAnnotator > *").remove(); + // Enable lasso interaction + lassoEnable(); + // Disable Schema Investigation + flagForSchema = false; + // Empty all the arrays that are related to Schema Investigation + Arrayx = []; + Arrayy = []; + XYDistId = []; + Arrayxy = []; + DistanceDrawing1D = []; + allTransformPoints = []; + pFinal = []; + ArrayLimit = []; + correlationResults = []; + ArrayContainsDataFeaturesLimit = []; + prevRightClick = false; + + // Reset the points into their initial state for (var i=0; i < InitialStatePoints.length; i++){ InitialStatePoints[i].selected = true; + InitialStatePoints[i].starplot = false; } redraw(InitialStatePoints); + } -function setLayerProj(){ +function setLayerProj(){ // The main Layer becomes the projection + d3.select("#modtSNEcanvas").style("z-index", 2); d3.select("#modtSNEcanvas_svg").style("z-index", 1); d3.select("#modtSNEcanvas_svg_Schema").style("z-index", 1); + d3.select("#SvgAnnotator").style("z-index", 1); + } -function setLayerComp(){ +function setLayerComp(){ // The main Layer becomes the comparison (starplot) + + d3.selectAll("#modtSNEcanvas_svg > *").remove(); d3.select("#modtSNEcanvas_svg").style("z-index", 2); d3.select("#modtSNEcanvas_svg_Schema").style("z-index", 1); d3.select("#modtSNEcanvas").style("z-index", 1); + d3.select("#SvgAnnotator").style("z-index", 1); + if (points.length){ lassoEnable(); } + + // Reset the points into their initial state + for (var i=0; i < InitialStatePoints.length; i++){ + InitialStatePoints[i].selected = true; + InitialStatePoints[i].starplot = false; + } + redraw(InitialStatePoints); + } -function setLayerSche(){ +function setLayerSche(){ // The main Layer becomes the correlation (barchart) + d3.select("#modtSNEcanvas_svg_Schema").style("z-index", 2); d3.select("#modtSNEcanvas").style("z-index", 1); d3.select("#modtSNEcanvas_svg").style("z-index", 1); + d3.select("#SvgAnnotator").style("z-index", 1); let c = 0; for (var i=0; i < points.length; i++){ points[i].selected = true; if (points[i].starplot == true){ c = c + 1; if (c == 1){ - alert("The starplot visualization will be lost!"); + alert("The starplot visualization will be lost!"); // Alert the user that the starplot will be lost! } points[i].starplot = false; } } redraw(points); click(); + if (prevRightClick == true){ + flagForSchema = true; + CalculateCorrel(); + } + } -function lassoEnable(){ +function lassoEnable(){ // The main Layer becomes the correlation (barchart) var interactionSvg = d3.select("#modtSNEcanvas_svg") .attr("width", dimensions) @@ -310,22 +375,23 @@ function lassoEnable(){ .style('left', 0); var lassoInstance = lasso() - .on('end', handleLassoEnd) - .on('start', handleLassoStart); + .on('end', handleLassoEnd) // Lasso ending point of the interaction + .on('start', handleLassoStart); // Lasso starting point of the interaction interactionSvg.call(lassoInstance); } +function setAnnotator(){ // Set a new annotation on top of the main visualization. -function setAnnotator(){ - - var viewport2 = getViewport(); + var viewport2 = getViewport(); // Get the main viewport width and height. var vw2 = viewport2[0]; var vh2 = viewport2[1]; var textarea = document.getElementById("comment").value; - var annotations = [ + d3.select("#SvgAnnotator").style("z-index", 3); + + var annotations = [ // Initialize the draggable ringNote. { "cx": 232, "cy": 123, @@ -338,7 +404,7 @@ function setAnnotator(){ } ]; - var ringNote = d3v3.ringNote() + var ringNote = d3v3.ringNote() // Make it draggable. .draggable(true); var svgAnnotator = d3v3.select("#SvgAnnotator") @@ -354,18 +420,16 @@ function setAnnotator(){ gAnnotations.selectAll(".annotation circle") .classed("shaded", function(d) { return d.shaded; }); - - ringNotes.push(ringNote); + ringNotes.push(ringNote); // Push all the ringNote and annotations and enable draggable property. gAnnotationsAll.push(gAnnotations); AnnotationsAll.push(annotations); draggable.push(true); } - // Hide or show the controls d3.select("#controls") .on("change", function() { - if(ringNotes[0]){ + if(ringNotes[0]){ // If at least one ringNote exists, then enable or disable the draggable and radius changing controllers. for (var i = 0; i < ringNotes.length; i++){ ringNotes[i].draggable(draggable[i] = !draggable[i]); gAnnotationsAll[i] @@ -383,63 +447,41 @@ function setAnnotator(){ } }); - // Three.js render loop - function animate() { - requestAnimationFrame(animate); - renderer.render(scene, camera); - } - - var MainCanvas; - var Child; - var renderer; - var fov = 21; - var near = 10; - var far = 7000; - var camera; - var scene; - - MainCanvas = document.getElementById('modtSNEcanvas'); - Child = document.getElementById('modtSNEDiv'); - - - // Add canvas - renderer = new THREE.WebGLRenderer({ canvas: MainCanvas }); - renderer.setSize(dimensions, dimensions); - Child.append(renderer.domElement); - - scene = new THREE.Scene(); - scene.background = new THREE.Color(0xffffff); - - // Set up camera and scene - camera = new THREE.PerspectiveCamera( - fov, - dimensions / dimensions, - near, - far - ); - animate(); - - var Arrayx = []; - var Arrayy = []; - var XYDistId = []; - var Arrayxy = []; - var DistanceDrawing1D = []; - var allTransformPoints = []; - var p; - var pFinal = []; - var paths; - var path; - var ArrayLimit = []; - var minimum; - var correlationResults = []; - var ArrayContainsDataFeaturesLimit = []; - + // Three.js render loop for the very first scene. + function animate() { + requestAnimationFrame(animate); + renderer.render(scene, camera); + } + MainCanvas = document.getElementById('modtSNEcanvas'); + Child = document.getElementById('modtSNEDiv'); + // Add main canvas + renderer = new THREE.WebGLRenderer({ canvas: MainCanvas }); + renderer.setSize(dimensions, dimensions); + Child.append(renderer.domElement); -// function that executes after data is successfully loaded -function init(data, results_all, fields) { + // Add a new empty (white) scene. + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xffffff); + // Set up camera. + camera = new THREE.PerspectiveCamera( + fov, + dimensions / dimensions, + near, + far + ); + // Animate the scene. + animate(); + +// The following function executes exactly after the data is successfully loaded. New EXECUTION! +// results_all variable is all the columns multiplied by all the rows. +// data variable is all the columns except strings, undefined values, or "Version" plus beta and cost values." +// fields variable is all the features (columns) plus beta and cost strings. +function init(data, results_all, fields) { + + // Remove all previously drawn SVGs d3.selectAll("#correlation > *").remove(); d3.selectAll("#modtSNEcanvas_svg > *").remove(); d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); @@ -447,49 +489,55 @@ function init(data, results_all, fields) { d3.selectAll("#sheparheat > *").remove(); d3.selectAll("#knnBarChart > *").remove(); + // Clear the previous t-SNE overview canvas. var oldcanvOver = document.getElementById('tSNEcanvas'); var contxOver = oldcanvOver.getContext('experimental-webgl'); contxOver.clear(contxOver.COLOR_BUFFER_BIT); + // Clear the previously drawn main visualization canvas. scene = new THREE.Scene(); scene.background = new THREE.Color(0xffffff); + // Clear all the legends that were drawn. d3.selectAll("#legend1 > *").remove(); + d3.selectAll("#legend2 > *").remove(); d3.selectAll("#legend3 > *").remove(); - d3.selectAll("#legend4 > *").remove(); + // Enable again the lasso interaction. lassoEnable(); + // Empty all the Schema Investigation arrays. Arrayx = []; Arrayy = []; XYDistId = []; Arrayxy = []; DistanceDrawing1D = []; allTransformPoints = []; - p; pFinal = []; - paths; - path; ArrayLimit = []; - minimum; correlationResults = []; ArrayContainsDataFeaturesLimit = []; - prevRightClick = false; + + // Step counter set to 0 step_counter = 0; + // Get the new parameters from the t-SNE parameters panel. max_counter = document.getElementById("param-maxiter-value").value; opt = {}; var fields; fields.push("beta"); fields.push("cost"); - opt.epsilon = document.getElementById("param-learningrate-value").value; // epsilon is learning rate (10 = default) - opt.perplexity = document.getElementById("param-perplexity-value").value; // roughly how many neighbors each point influences (30 = default) - tsne = new tsnejs.tSNE(opt); + opt.epsilon = document.getElementById("param-learningrate-value").value; // Epsilon is learning rate (10 = default) + opt.perplexity = document.getElementById("param-perplexity-value").value; // Roughly how many neighbors each point influences (30 = default) + tsne = new tsnejs.tSNE(opt); // Set new t-SNE with specific perplexity. + + // Put the input variables into more properly named variables and store them. final_dataset = data; dataFeatures = results_all; - var object; + var object; all_labels = []; + // Get the dimension that contains an asterisk mark ("*"). This is our classification label. dataFeatures.filter(function(obj) { var temp = []; @@ -504,61 +552,62 @@ function init(data, results_all, fields) { }); for (let k = 0; k < dataFeatures.length; k++){ + ArrayContainsDataFeatures.push(Object.values(dataFeatures[k]).concat(k)); object = []; for (let j = 0; j < Object.keys(dataFeatures[k]).length; j++){ - if(typeof(Object.values(dataFeatures[k])[j]) == "number" && Object.keys(dataFeatures[k])[j] != Category){ + if(typeof(Object.values(dataFeatures[k])[j]) == "number" && Object.keys(dataFeatures[k])[j] != Category){ // Only numbers and not the classification labels. object.push(Object.values(dataFeatures[k])[j]); } } - ArrayContainsDataFeaturesCleared.push(object); + ArrayContainsDataFeaturesCleared.push(object.concat(k)); // The ArrayContainsDataFeaturesCleared contains only numbers without the categorization parameter even if it is a number. + } - + var valCategExists = 0; for (var i=0; i max_dist) max_dist = dist[i][j]; - } + + var max_dist = 0; + for(var i = 0; i < data.length; i++) { + for(var j = i + 1; j < data.length; j++) { + if(dist[i][j] > max_dist) max_dist = dist[i][j]; } - for(var i = 0; i < data.length; i++) { - for(var j = 0; j < data.length; j++) { - dist[i][j] /= max_dist; } + for(var i = 0; i < data.length; i++) { + for(var j = 0; j < data.length; j++) { + dist[i][j] /= max_dist; } - return dist; + } + return dist; + } +// No tranformation function noTrans(data) { - return data; + return data; } -// Log transform +// Log tranformation function logTrans(data) { - for(var i = 0; i < data.length; i++) { - for(var d in data[0]) { - if(d != "name") { - X = data[i][d]; - data[i][d] = Math.log10(X + 1); + + for(var i = 0; i < data.length; i++) { + for(var d in data[0]) { + if(d != Category) { + X = data[i][d]; + data[i][d] = Math.log10(X + 1); } - } } - return data; + } + return data; + } -// asinh transform +// Asinh tranformation function asinhTrans(data) { - for(var i = 0; i < data.length; i++) { - for(var d in data[0]) { - if(d != "name") { - X = data[i][d]; - data[i][d] = Math.log(X + Math.sqrt(X * X + 1)); + + for(var i = 0; i < data.length; i++) { + for(var d in data[0]) { + if(d != Category) { + X = data[i][d]; + data[i][d] = Math.log(X + Math.sqrt(X * X + 1)); } - } } - return data; + } + return data; + } -// binarize +// Binarize tranformation function binTrans(data) { - for(var i = 0; i < data.length; i++) { - for(var d in data[0]) { - if(d != "name") { - X = data[i][d]; - if(X > 0) data[i][d] = 1; - if(X < 0) data[i][d] = 0; + + for(var i = 0; i < data.length; i++) { + for(var d in data[0]) { + if(d != Category) { + X = data[i][d]; + if(X > 0) data[i][d] = 1; + if(X < 0) data[i][d] = 0; } - } } - return data; + } + return data; + } +// Compute the distances by applying the chosen distance functions and transformation functions. function computeDistances(data, distFunc, transFunc) { - dist = eval(distFunc)(eval(transFunc)(data)); - dist = normDist(data, dist); - return dist; + + dist = eval(distFunc)(eval(transFunc)(data)); + dist = normDist(data, dist); + return dist; + } -// function that updates embedding +// Function that updates embedding function updateEmbedding(AnalaysisResults) { - if (AnalaysisResults == ""){ - var Y = tsne.getSolution(); // here we get the solution from the actual t-sne + + if (AnalaysisResults == ""){ // Check if the embedding does not need to load because we had a previous analysis uploaded. + var Y = tsne.getSolution(); // We receive the solution from the t-SNE var xExt = d3.extent(Y, d => d[0]); var yExt = d3.extent(Y, d => d[1]); var maxExt = [Math.min(xExt[0], yExt[0]), Math.max(xExt[1], yExt[1])]; - var x = d3.scaleLinear() + var x = d3.scaleLinear() // Scale the x points into the canvas width/height .domain(maxExt) .range([10, +dimensions-10]); - var y = d3.scaleLinear() + var y = d3.scaleLinear() // Scale the y points into the canvas width/height .domain(maxExt) .range([10, +dimensions-10]); for(var i = 0; i < final_dataset.length; i++) { - - x_position[i] = x(Y[i][0]); - y_position[i] = y(Y[i][1]); - points[i] = {id: i, x: x_position[i], y: y_position[i], beta: final_dataset[i].beta, cost: final_dataset[i].cost, selected: true, DimON: null, starplot: false}; - points2d[i] = {id: i, x: x_position[i], y: y_position[i], selected: true}; + x_position[i] = x(Y[i][0]); // x points position + y_position[i] = y(Y[i][1]); // y points position + points[i] = {id: i, x: x_position[i], y: y_position[i], beta: final_dataset[i].beta, cost: final_dataset[i].cost, selected: true, DimON: null, starplot: false}; // Create the points and points2D (2 dimensions) + points2d[i] = {id: i, x: x_position[i], y: y_position[i], selected: true}; // and add everything that we know about the points (e.g., selected = true, starplot = false in the beginning and so on) points[i] = extend(points[i], ArrayContainsDataFeaturesCleared[i]); points[i] = extend(points[i], dataFeatures[i]); } - } else{ - points = AnalaysisResults.slice(0,dataFeatures.length); - points2d = AnalaysisResults.slice(dataFeatures.length,2*dataFeatures.length); - overallCost = AnalaysisResults.slice(dataFeatures.length*2,dataFeatures.length*2+1); - ParametersSet = AnalaysisResults.slice(dataFeatures.length*2+1, dataFeatures.length*2+7); + } else{ + points = AnalaysisResults.slice(0,dataFeatures.length); // Load the points from the previous analysis + points2d = AnalaysisResults.slice(dataFeatures.length,2*dataFeatures.length); // Load the 2D points + overallCost = AnalaysisResults.slice(dataFeatures.length*2,dataFeatures.length*2+1); // Load the overall cost + ParametersSet = AnalaysisResults.slice(dataFeatures.length*2+1, dataFeatures.length*2+7); // Load the parameters and set the necessary values to the visualization of those parameters. $("#cost").html("Number of Iteration: " + ParametersSet[3] + ", Overall Cost: " + overallCost); $('#param-perplexity-value').text(ParametersSet[1]); $('#param-learningrate-value').text(ParametersSet[2]); $('#param-maxiter-value').text(ParametersSet[3]); document.getElementById("param-distance").value = ParametersSet[4]; document.getElementById("param-transform").value = ParametersSet[5]; - } - InitialStatePoints = points; - function extend(obj, src) { - for (var key in src) { - if (src.hasOwnProperty(key)) obj[key] = src[key]; - } - return obj; - } - ShepardHeatMap(); - OverviewtSNE(points); - BetatSNE(points); + } + InitialStatePoints = points; // Initial Points will not be modified! + + function extend(obj, src) { // Call this function to add additional information to the points such as dataFeatures and Array which contains the data features without strings. + for (var key in src) { + if (src.hasOwnProperty(key)) obj[key] = src[key]; + } + return obj; // Return the different forms of the same data that we eventually store on those points. + } + + // Run all the main functions (Shepard Heatmap, Overview t-SNE, and Beta/Cost t-SNE) Beta = 1/sigma, Cost = KLD(Q||P). + ShepardHeatMap(); + OverviewtSNE(points); + BetatSNE(points); + } function ShepardHeatMap () { + + // Remove any previous shepard heatmaps. d3.selectAll("#sheparheat > *").remove(); + + // Set the margin of the shepard heatmap var margin = { top: 35, right: 15, bottom: 15, left: 35 }, dim2 = Math.min(parseInt(d3.select("#sheparheat").style("width")), parseInt(d3.select("#sheparheat").style("height"))) width = dim2- margin.left - margin.right, height = dim2 - margin.top - margin.bottom, - buckets = 10, + buckets = 10, // Set the number of buckets. gridSize = width / buckets, - dim_1 = ["0.0", "0.2", "0.4", "0.6", "0.8", "1.0"], - dim_2 = ["0.0", "0.4", "0.6", "1.0"] - - // Create the svg canvas - var svg = d3.select("#sheparheat") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); -dists2d = computeDistances(points2d, document.getElementById("param-distance").value, document.getElementById("param-transform").value); - -var dist_list2d = []; + dim_1 = ["0.0", "0.2", "0.4", "0.6", "0.8", "1.0"], // Set the dimensions for the output and input distances. + dim_2 = ["0.0", "0.4", "0.6", "1.0"] //I.e., the axes. + + // Create the svg for the shepard heatmap + var svg = d3.select("#sheparheat") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + // Calculate the 2D distances. + dists2d = computeDistances(points2d, document.getElementById("param-distance").value, document.getElementById("param-transform").value); + +var dist_list2d = []; // Distances lists empty var dist_list = []; - for (var j=0; j data[l-1].dim2/10){ - counnum[l] = counnum[l] + 1; - } - }else if (l % 10 == 1){ - temp_loop = data[l].dim1-1; - if (dist_list_all[0][k] < data[l].dim1/10 && dist_list_all[1][k] < data[l].dim2/10 && dist_list_all[0][k] > temp_loop/10){ - counnum[l] = counnum[l] + 1; - } - }else{ - if (dist_list_all[0][k] <= data[l].dim1/10 && dist_list_all[1][k] <= data[l].dim2/10 && dist_list_all[1][k] >= data[l-1].dim2/10 && dist_list_all[0][k] > temp_loop/10){ - counnum[l] = counnum[l] + 1; + var counter = 0; + var counnum = []; + var temp_loop = 0; + for (var l=0; l<100; l++) {counnum[l] = 0}; + var dist_list_all = []; + dist_list_all =[dist_list, dist_list2d]; // Combine the two lists. + + for (var l=0; l<100; l++){ // Here we calculate the shepard diagram and then we add the colors! -> shepard heatmap + for (k=0; k data[l-1].dim2/10){ + counnum[l] = counnum[l] + 1; + } + }else if (l % 10 == 1){ + temp_loop = data[l].dim1-1; + if (dist_list_all[0][k] < data[l].dim1/10 && dist_list_all[1][k] < data[l].dim2/10 && dist_list_all[0][k] > temp_loop/10){ + counnum[l] = counnum[l] + 1; + } + }else{ + if (dist_list_all[0][k] <= data[l].dim1/10 && dist_list_all[1][k] <= data[l].dim2/10 && dist_list_all[1][k] >= data[l-1].dim2/10 && dist_list_all[0][k] > temp_loop/10){ + counnum[l] = counnum[l] + 1; + } } } + counter = counter + counnum[l]; } - counter = counter + counnum[l]; - } - for (var m=0; m" + Math.round(d.value); - }); + for (var m=0; m" + Math.round(d.value); + }); + + tip(svg.append("g")); + + var dim1Labels = svg.selectAll(".dim1Label") // Label + .data(dim_1) + .enter().append("text") + .text(function (d) { return d; }) + .attr("x", 0) + .attr("y", function (d, i) { return i * gridSize * 2; }) + .style("text-anchor", "end") + .style("font-size", "10px") + .attr("transform", "translate(-6," + gridSize / 4 + ")") + .attr("class","mono"); + + var title = svg.append("text") // Title = Input Distance + .attr("class", "mono") + .attr("x", -(gridSize * 7)) + .attr("y", -26) + .style("font-size", "12px") + .attr("transform", "rotate(-90)") + .attr("class","mono") + .text("Input Distance"); + + + var title = svg.append("text") // Title = Output Distance + .attr("class", "mono") + .attr("x", gridSize * 3 ) + .attr("y", -26) + .style("font-size", "12px") + .text("Output Distance"); + + var dim2Labels = svg.selectAll(".dim2Label") // Label + .data(dim_2) + .enter().append("text") + .text(function(d) { return d; }) + .attr("x", function(d, i) { return i * gridSize * 3.2; }) + .attr("y", 0) + .style("text-anchor", "middle") + .style("font-size", "10px") + .attr("transform", "translate(" + gridSize / 4 + ", -6)") + .attr("class","mono"); + + var heatMap = svg.selectAll(".dim2") // Combine the two dimensions and plot the shepard heatmap + .data(data) + .enter().append("rect") + .attr("x", function(d) { return (d.dim2 - 1) * gridSize; }) + .attr("y", function(d) { return (d.dim1 - 1) * gridSize; }) + .attr("rx", 0.4) + .attr("ry", 0.4) + .attr("class", "dim2 bordered") + .attr("width", gridSize-2) + .attr("height", gridSize-2) + .style("fill", colors[0]) + .attr("class", "square") + .on('mouseover', tip.show) + .on('mouseout', tip.hide); + + heatMap.transition() + .style("fill", function(d) { return colorScale(d.value); }); + + heatMap.append("title").text(function(d) { return d.value; }); + + var heatleg = d3.select("#legend3"); // Legend3 = the legend of the shepard heatmap + + heatleg.append("g") + .attr("class", "legendLinear") + .attr("transform", "translate(0,14)"); + + var legend = d3.legendColor() // Legend color and title! + .labelFormat(d3.format(",.0f")) + .cells(9) + .title("Number of Points") + .scale(colorScale); - heatleg.select(".legendLinear") - .call(legend); - }); + heatleg.select(".legendLinear") + .call(legend); + }); } // perform single t-SNE iteration @@ -915,24 +995,24 @@ function step() { } } -function resize(canvas) { +function resize(canvas) { // This is being used in the WebGL t-SNE for the overview canvas + // Lookup the size the browser is displaying the canvas. var displayWidth = canvas.clientWidth; var displayHeight = canvas.clientHeight; // Check if the canvas is not the same size. - if (canvas.width != displayWidth || - canvas.height != displayHeight) { - + if (canvas.width != displayWidth || canvas.height != displayHeight) { // Make the canvas the same size canvas.width = displayWidth; canvas.height = displayHeight; } + } -function OverviewtSNE(points){ +function OverviewtSNE(points){ // The overview t-SNE function - var canvas = document.getElementById('tSNEcanvas'); + var canvas = document.getElementById('tSNEcanvas'); // WebGL & canvas gl = canvas.getContext('experimental-webgl'); // If we don't have a GL context, give up now @@ -941,28 +1021,27 @@ function OverviewtSNE(points){ return; } - ColorsCategorical = ['#a6cee3','#fb9a99','#b2df8a','#33a02c','#1f78b4','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a']; + ColorsCategorical = ['#a6cee3','#fb9a99','#b2df8a','#33a02c','#1f78b4','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a']; // Colors for the labels/categories if there are some! if (all_labels[0] == undefined){ - var colorScale = d3.scaleOrdinal().domain(["No Category"]).range(["#C0C0C0"]); - } - else{ - var colorScale = d3.scaleOrdinal().domain(all_labels).range(ColorsCategorical); + var colorScale = d3.scaleOrdinal().domain(["No Category"]).range(["#C0C0C0"]); // If no category then grascale. + } else{ + var colorScale = d3.scaleOrdinal().domain(all_labels).range(ColorsCategorical); // We use the color scale here! } - d3.select("#legend3").select("svg").remove(); - var svg = d3.select("#legend3").append("svg"); + d3.select("#legend2").select("svg").remove(); // Create the legend2 which is for the overview panel. + var svg = d3.select("#legend2").append("svg"); - svg.append("g") - .attr("class", "legendOrdinal") - .attr("transform", "translate(8,5)"); + svg.append("g") + .attr("class", "legendOrdinal") + .attr("transform", "translate(8,5)"); - var legendOrdinal = d3.legendColor() - .shape("path", d3.legendSize(100)) - .shapePadding(15) - .scale(colorScale); + var legendOrdinal = d3.legendColor() + .shape("path", d3.legendSize(100)) + .shapePadding(15) + .scale(colorScale); - svg.select(".legendOrdinal") - .call(legendOrdinal); + svg.select(".legendOrdinal") + .call(legendOrdinal); let vertices = []; let colors = []; @@ -970,35 +1049,35 @@ function OverviewtSNE(points){ for (var i=0; i1 to 0->2 - let zeroToTwo = zeroToOne * 2.0; - let zeroToTwo2 = zeroToOne2 * 2.0; - - // convert from 0->2 to -1->+1 (clipspace) - let clipSpace = zeroToTwo - 1.0; - let clipSpace2 = zeroToTwo2 - 1.0; - singleObj = clipSpace; - vertices.push(singleObj); - singleObj = clipSpace2 * -1; - vertices.push(singleObj); - singleObj = 0.0; - vertices.push(singleObj); + let zeroToOne = points[i].x / dimensions; + let zeroToOne2 = points[i].y / dimensions; + + // convert from 0->1 to 0->2 + let zeroToTwo = zeroToOne * 2.0; + let zeroToTwo2 = zeroToOne2 * 2.0; + + // convert from 0->2 to -1->+1 (clipspace) + let clipSpace = zeroToTwo - 1.0; + let clipSpace2 = zeroToTwo2 - 1.0; + singleObj = clipSpace; + vertices.push(singleObj); + singleObj = clipSpace2 * -1; + vertices.push(singleObj); + singleObj = 0.0; + vertices.push(singleObj); } for (var i=0; i Arrayxy. + } - svgClick.on("contextmenu", function (d) { + for (var k = 0; k < Arrayxy.length - 1 ; k++){ // Draw the line which connects two circles + d3.select('#modtSNEcanvas_svg_Schema').append('line') + .attr("x1", Arrayxy[k][0]) + .attr("y1", Arrayxy[k][1]) + .attr("x2", Arrayxy[k+1][0]) + .attr("y2", Arrayxy[k+1][1]) + .style("stroke","black") + .style("stroke-width",1); + } - if (prevRightClick == true){ + }); - } else { + svgClick.on("contextmenu", function (d) { + if (prevRightClick == true){ // Do not do anything because the right click should be prevented + } else { + var line = d3.line().curve(d3.curveCardinal); - var line = d3.line().curve(d3.curveCardinal); + for (var k = 0; k < Arrayxy.length - 1; k++){ // Define a path and check the schema. + path = svgClick.append("path") + .datum(Arrayxy.slice(k, k+2)) + .attr("class", "SchemaCheck") + .attr("d", line); + } + // Prevent the default mouse action. Allow right click to be used for the confirmation of our schema. + d3.event.preventDefault(); - for (var k = 0; k < Arrayxy.length - 1; k++){ - path = svgClick.append("path") - .datum(Arrayxy.slice(k, k+2)) - .attr("class", "SchemaCheck") - .attr("d", line); - } - // Prevent the default mouse action. Allow right click to be used for the confirmation of our schema. - d3.event.preventDefault(); + flagForSchema = true; // Schema is activated. + CalculateCorrel(); // Calculate the correlations + } + }); - flagForSchema = true; - CalculateCorrel(); - } - }); } -function CalculateCorrel(){ +function CalculateCorrel(){ // Calculate the correlation is a function which has all the computations for the schema ordering (investigation). if (flagForSchema == false){ - alert("Please, draw a schema first!"); + alert("Please, draw a schema first!"); // If no Schema is drawn then ask the user! } else{ - var correlLimit = document.getElementById("param-corr-value").value; - correlLimit = parseInt(correlLimit); + var correlLimit = document.getElementById("param-corr-value").value; // Get the threshold value with which the user set's the boundaries of the schema investigation + correlLimit = parseInt(correlLimit); allTransformPoints = []; for (var loop = 0; loop < points.length ; loop++){ @@ -1290,73 +1363,74 @@ function CalculateCorrel(){ paths = svgClick.selectAll("path").filter(".SchemaCheck"); XYDistId = []; - if (paths.nodes().length == 0){ + if (paths.nodes().length == 0){ // We need more than 1 points alert("Please, provide one more point in order to create a line (i.e., path)!") } else{ for (var m = 0; m < paths.nodes().length; m++) { - for (var j = 0; j < allTransformPoints.length; j++){ - p = closestPoint(paths.nodes()[m], allTransformPoints[j]); - XYDistId.push(p); + for (var j = 0; j < allTransformPoints.length; j++){ + p = closestPoint(paths.nodes()[m], allTransformPoints[j]); // Closest of each point to the paths that we have. + XYDistId.push(p); // Take the XY coordinates, Distance, and ID + } } - } for (var j = 0; j < allTransformPoints.length; j++){ - for (var m = 0; m < paths.nodes().length; m++) { - if (m == 0){ - minimum = XYDistId[j].distance; - } - else if (minimum > XYDistId[(m * allTransformPoints.length) + j].distance) { - minimum = XYDistId[(m * allTransformPoints.length) + j].distance; + for (var m = 0; m < paths.nodes().length; m++) { // Find the minimum path distance for each point + if (m == 0){ + minimum = XYDistId[j].distance; + } + else if (minimum > XYDistId[(m * allTransformPoints.length) + j].distance) { + minimum = XYDistId[(m * allTransformPoints.length) + j].distance; + } } - } - for (var l = 0; l < paths.nodes().length ; l++) { - if (XYDistId[(l * allTransformPoints.length) + j].distance == minimum){ - allTransformPoints[j].bucketID = l; + for (var l = 0; l < paths.nodes().length ; l++) { + if (XYDistId[(l * allTransformPoints.length) + j].distance == minimum){ + allTransformPoints[j].bucketID = l; // Bucket ID in which each point belongs to... + } } } - } var arrays = [], size = allTransformPoints.length; - while (XYDistId.length > 0) { + while (XYDistId.length > 0) { // For each path I have all the necessary information (all the IDs of the points etc..) arrays.push(XYDistId.splice(0, size)); } - + var arraysCleared = []; - for (var j = 0; j < allTransformPoints.length; j++){ + + for (var j = 0; j < allTransformPoints.length; j++){ // Now we have the XY coordinates values of the points, the IDs of the points, the xy coordinates on the line, the number of the path that they belong two times. for (var m=0; m < arrays.length; m++) { if (allTransformPoints[j].bucketID == m){ arraysCleared.push(arrays[m][j].concat(allTransformPoints[j].bucketID, Arrayxy[m], arrays[m][j].distance, arrays[m][j].id)); } } } - + ArrayLimit = []; + for (var i=0; i *").remove(); d3.selectAll("#modtSNEcanvas_svg > *").remove(); - flagForSchema = false; - d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); + d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); + flagForSchema = false; Arrayx = []; Arrayy = []; XYDistId = []; Arrayxy = []; DistanceDrawing1D = []; allTransformPoints = []; - p; pFinal = []; - paths; - path; ArrayLimit = []; - minimum; correlationResults = []; ArrayContainsDataFeaturesLimit = []; prevRightClick = false; + for (var i=0; i < InitialStatePoints.length; i++){ InitialStatePoints[i].selected = true; InitialStatePoints[i].starplot = false; } - alert("No points selected! Please, try to increase the correlation threshold."); redraw(InitialStatePoints); + + alert("No points selected! Please, try to increase the correlation threshold."); } else { for (var loop = 0; loop < ArrayContainsDataFeaturesLimit.length; loop++) { ArrayContainsDataFeaturesLimit[loop].push(loop); @@ -1445,11 +1531,11 @@ function CalculateCorrel(){ ); if (isNaN(pearsonCorrelation(tempData, 0, 1))) { } else{ - SignStore.push([temp, pearsonCorrelation(tempData, 0, 1)]); - correlationResults.push([Object.keys(dataFeatures[0])[temp] + " (" + temp + ")", Math.abs(pearsonCorrelation(tempData, 0, 1))]); + SignStore.push([temp, pearsonCorrelation(tempData, 0, 1)]); // Keep the sign + correlationResults.push([Object.keys(dataFeatures[0])[temp] + " (" + temp + ")", Math.abs(pearsonCorrelation(tempData, 0, 1))]); // Find the pearson correlations } } - correlationResults = correlationResults.sort( + correlationResults = correlationResults.sort( // Sort the correlations from the biggest to the lowest value (absolute values) function(a,b) { if (a[1] == b[1]) return a[0] < b[0] ? -1 : 1; @@ -1460,192 +1546,195 @@ function CalculateCorrel(){ for (var j = 0; j < correlationResults.length; j++) { for (var i = 0; i < SignStore.length; i++) { if (SignStore[i][1]*(-1) == correlationResults[j][1]) { - correlationResults[j][1] = parseInt(correlationResults[j][1] * 100) * (-1); + correlationResults[j][1] = parseInt(correlationResults[j][1] * 100) * (-1); // Give the negative sign if needed and multiply by 100 correlationResults[j].push(j); } if (SignStore[i][1] == correlationResults[j][1]) { - correlationResults[j][1] = parseInt(correlationResults[j][1] * 100); + correlationResults[j][1] = parseInt(correlationResults[j][1] * 100); // Give a positive sign and multiply by 100 correlationResults[j].push(j); } } } } - drawBarChart(); + drawBarChart(); // Draw the horizontal barchart with the correlations. } } } -function drawBarChart(){ - d3.selectAll("#correlation > *").remove(); - ///////////////////////////////////////////////////////////// - ///////////////// Set-up SVG and wrappers /////////////////// - ///////////////////////////////////////////////////////////// - +function drawBarChart(){ // Draw the horizontal barchart with the correlations. - var mainGroup = svg.append("g") - .attr("class","mainGroupWrapper") - .attr("transform","translate(" + main_margin.left + "," + main_margin.top + ")") - .append("g") //another one for the clip path - due to not wanting to clip the labels - .attr("clip-path", "url(#clip)") - .style("clip-path", "url(#clip)") - .attr("class","mainGroup") - - var miniGroup = svg.append("g") - .attr("class","miniGroup") - .attr("transform","translate(" + (main_margin.left + main_width + main_margin.right + mini_margin.left) + "," + mini_margin.top + ")"); - - var brushGroup = svg.append("g") - .attr("class","brushGroup") - .attr("transform","translate(" + (main_margin.left + main_width + main_margin.right + mini_margin.left) + "," + mini_margin.top + ")"); - - ///////////////////////////////////////////////////////////// - ////////////////////// Initiate scales ////////////////////// - ///////////////////////////////////////////////////////////// + // Remove any previous barchart. + d3.selectAll("#correlation > *").remove(); - main_xScale = d3v3.scale.linear().range([0, main_width]); - mini_xScale = d3v3.scale.linear().range([0, mini_width]); - - main_yScale = d3v3.scale.ordinal().rangeBands([0, main_height], 0.4, 0); - mini_yScale = d3v3.scale.ordinal().rangeBands([0, mini_height], 0.4, 0); - //Based on the idea from: http://stackoverflow.com/questions/21485339/d3-brushing-on-grouped-bar-chart - main_yZoom = d3v3.scale.linear() - .range([0, main_height]) - .domain([0, main_height]); - - //Create x axis object - main_xAxis = d3v3.svg.axis() - .scale(main_xScale) - .orient("bottom") - .ticks(8) - .outerTickSize(0); - - //Add group for the x axis - d3v3.select(".mainGroupWrapper").append("g") - .attr("class", "x axis") - .attr("transform", "translate(" + 0 + "," + (main_height + 5) + ")"); - - //Create y axis object - main_yAxis = d3v3.svg.axis() - .scale(main_yScale) - .orient("left") - .tickSize(0) - .outerTickSize(0); - - - - //Add group for the y axis - mainGroup.append("g") - .attr("class", "y axis") - .attr("transform", "translate(-5,0)"); - - ///////////////////////////////////////////////////////////// - /////////////////////// Update scales /////////////////////// - ///////////////////////////////////////////////////////////// - //Update the scales - main_xScale.domain([-100, 100]); - mini_xScale.domain([-100, 100]); - main_yScale.domain(correlationResults.map(function(d) { return d[0]; })); - mini_yScale.domain(correlationResults.map(function(d) { return d[0]; })); - - //Create the visual part of the y axis - d3v3.select(".mainGroup").select(".y.axis").call(main_yAxis); - d3v3.select(".mainGroupWrapper").select(".x.axis").call(main_xAxis); - - ///////////////////////////////////////////////////////////// - ///////////////////// Label axis scales ///////////////////// - ///////////////////////////////////////////////////////////// - - textScale = d3v3.scale.linear() - .domain([15,50]) - .range([12,6]) - .clamp(true); - - ///////////////////////////////////////////////////////////// - ///////////////////////// Create brush ////////////////////// - ///////////////////////////////////////////////////////////// - - //What should the first extent of the brush become - a bit arbitrary this - var brushExtent = parseInt(Math.max( 1, Math.min( 20, Math.round(correlationResults.length * 0.75) ) )); - - brush = d3v3.svg.brush() - .y(mini_yScale) - .extent([mini_yScale(correlationResults[0][0]), mini_yScale(correlationResults[brushExtent][0])]) - .on("brush", brushmove) - - //Set up the visual part of the brush - gBrush = d3v3.select(".brushGroup").append("g") - .attr("class", "brush") - .call(brush); - - gBrush.selectAll(".resize") - .append("line") - .attr("x2", mini_width); - - gBrush.selectAll(".resize") - .append("path") - .attr("d", d3v3.svg.symbol().type("triangle-up").size(20)) - .attr("transform", function(d,i) { - return i ? "translate(" + (mini_width/2) + "," + 4 + ") rotate(180)" : "translate(" + (mini_width/2) + "," + -4 + ") rotate(0)"; - }); + ///////////////////////////////////////////////////////////// + ///////////////// Set-up SVG and wrappers /////////////////// + ///////////////////////////////////////////////////////////// - gBrush.selectAll("rect") - .attr("width", mini_width); - //On a click recenter the brush window - gBrush.select(".background") - .on("mousedown.brush", brushcenter) - .on("touchstart.brush", brushcenter); - /////////////////////////////////////////////////////////////////////////// - /////////////////// Create a rainbow gradient - for fun /////////////////// - /////////////////////////////////////////////////////////////////////////// - - defs = svg.append("defs") - - //Create two separate gradients for the main and mini bar - just because it looks fun - createGradient("gradient-main", "60%"); - createGradient("gradient-mini", "13%"); - - //Add the clip path for the main bar chart - defs.append("clipPath") - .attr("id", "clip") - .append("rect") - .attr("x", -main_margin.left) - .attr("width", main_width + main_margin.left) - .attr("height", main_height); - - ///////////////////////////////////////////////////////////// - /////////////// Set-up the mini bar chart /////////////////// - ///////////////////////////////////////////////////////////// - - //The mini brushable bar - //DATA JOIN - var mini_bar = d3v3.select(".miniGroup").selectAll(".bar") - .data(correlationResults, function(d) { return +d[2]; }); - - //UDPATE - mini_bar - .attr("width", function(d) { return Math.abs(mini_xScale(d[1]) - mini_xScale(0)); }) - .attr("y", function(d,i) { return mini_yScale(d[0]); }) - .attr("height", mini_yScale.rangeBand()) - - //ENTER - mini_bar.enter().append("rect") - .attr("class", "bar") - .attr("x", function (d) { return mini_xScale(Math.min(0, d[1])); }) - .attr("width", function(d) { return Math.abs(mini_xScale(d[1]) - mini_xScale(0)); }) - .attr("y", function(d,i) { return mini_yScale(d[0]); }) - .attr("height", mini_yScale.rangeBand()) - .style("fill", "url(#gradient-mini)"); - - //EXIT - mini_bar.exit() - .remove(); - - //Start the brush - //gBrush.call(brush.event); - gBrush.call(brush.event); - prevRightClick = true; - } + var mainGroup = svg.append("g") + .attr("class","mainGroupWrapper") + .attr("transform","translate(" + main_margin.left + "," + main_margin.top + ")") + .append("g") //another one for the clip path - due to not wanting to clip the labels + .attr("clip-path", "url(#clip)") + .style("clip-path", "url(#clip)") + .attr("class","mainGroup") + + var miniGroup = svg.append("g") + .attr("class","miniGroup") + .attr("transform","translate(" + (main_margin.left + main_width + main_margin.right + mini_margin.left) + "," + mini_margin.top + ")"); + + var brushGroup = svg.append("g") + .attr("class","brushGroup") + .attr("transform","translate(" + (main_margin.left + main_width + main_margin.right + mini_margin.left) + "," + mini_margin.top + ")"); + + ///////////////////////////////////////////////////////////// + ////////////////////// Initiate scales ////////////////////// + ///////////////////////////////////////////////////////////// + + main_xScale = d3v3.scale.linear().range([0, main_width]); + mini_xScale = d3v3.scale.linear().range([0, mini_width]); + + main_yScale = d3v3.scale.ordinal().rangeBands([0, main_height], 0.4, 0); + mini_yScale = d3v3.scale.ordinal().rangeBands([0, mini_height], 0.4, 0); + //Based on the idea from: http://stackoverflow.com/questions/21485339/d3-brushing-on-grouped-bar-chart + main_yZoom = d3v3.scale.linear() + .range([0, main_height]) + .domain([0, main_height]); + + //Create x axis object + main_xAxis = d3v3.svg.axis() + .scale(main_xScale) + .orient("bottom") + .ticks(8) + .outerTickSize(0); + + //Add group for the x axis + d3v3.select(".mainGroupWrapper").append("g") + .attr("class", "x axis") + .attr("transform", "translate(" + 0 + "," + (main_height + 5) + ")"); + + //Create y axis object + main_yAxis = d3v3.svg.axis() + .scale(main_yScale) + .orient("left") + .tickSize(0) + .outerTickSize(0); + + + + //Add group for the y axis + mainGroup.append("g") + .attr("class", "y axis") + .attr("transform", "translate(-5,0)"); + + ///////////////////////////////////////////////////////////// + /////////////////////// Update scales /////////////////////// + ///////////////////////////////////////////////////////////// + //Update the scales + main_xScale.domain([-100, 100]); + mini_xScale.domain([-100, 100]); + main_yScale.domain(correlationResults.map(function(d) { return d[0]; })); + mini_yScale.domain(correlationResults.map(function(d) { return d[0]; })); + + //Create the visual part of the y axis + d3v3.select(".mainGroup").select(".y.axis").call(main_yAxis); + d3v3.select(".mainGroupWrapper").select(".x.axis").call(main_xAxis); + + ///////////////////////////////////////////////////////////// + ///////////////////// Label axis scales ///////////////////// + ///////////////////////////////////////////////////////////// + + textScale = d3v3.scale.linear() + .domain([15,50]) + .range([12,6]) + .clamp(true); + + ///////////////////////////////////////////////////////////// + ///////////////////////// Create brush ////////////////////// + ///////////////////////////////////////////////////////////// + + //What should the first extent of the brush become - a bit arbitrary this + var brushExtent = parseInt(Math.max( 1, Math.min( 20, Math.round(correlationResults.length * 0.75) ) )); + + brush = d3v3.svg.brush() + .y(mini_yScale) + .extent([mini_yScale(correlationResults[0][0]), mini_yScale(correlationResults[brushExtent][0])]) + .on("brush", brushmove) + + //Set up the visual part of the brush + gBrush = d3v3.select(".brushGroup").append("g") + .attr("class", "brush") + .call(brush); + + gBrush.selectAll(".resize") + .append("line") + .attr("x2", mini_width); + + gBrush.selectAll(".resize") + .append("path") + .attr("d", d3v3.svg.symbol().type("triangle-up").size(20)) + .attr("transform", function(d,i) { + return i ? "translate(" + (mini_width/2) + "," + 4 + ") rotate(180)" : "translate(" + (mini_width/2) + "," + -4 + ") rotate(0)"; + }); + + gBrush.selectAll("rect") + .attr("width", mini_width); + + //On a click recenter the brush window + gBrush.select(".background") + .on("mousedown.brush", brushcenter) + .on("touchstart.brush", brushcenter); + /////////////////////////////////////////////////////////////////////////// + /////////////////// Create a rainbow gradient - for fun /////////////////// + /////////////////////////////////////////////////////////////////////////// + + defs = svg.append("defs") + + //Create two separate gradients for the main and mini bar - just because it looks fun + createGradient("gradient-main", "60%"); + createGradient("gradient-mini", "13%"); + + //Add the clip path for the main bar chart + defs.append("clipPath") + .attr("id", "clip") + .append("rect") + .attr("x", -main_margin.left) + .attr("width", main_width + main_margin.left) + .attr("height", main_height); + + ///////////////////////////////////////////////////////////// + /////////////// Set-up the mini bar chart /////////////////// + ///////////////////////////////////////////////////////////// + + //The mini brushable bar + //DATA JOIN + var mini_bar = d3v3.select(".miniGroup").selectAll(".bar") + .data(correlationResults, function(d) { return +d[2]; }); + + //UDPATE + mini_bar + .attr("width", function(d) { return Math.abs(mini_xScale(d[1]) - mini_xScale(0)); }) + .attr("y", function(d,i) { return mini_yScale(d[0]); }) + .attr("height", mini_yScale.rangeBand()) + + //ENTER + mini_bar.enter().append("rect") + .attr("class", "bar") + .attr("x", function (d) { return mini_xScale(Math.min(0, d[1])); }) + .attr("width", function(d) { return Math.abs(mini_xScale(d[1]) - mini_xScale(0)); }) + .attr("y", function(d,i) { return mini_yScale(d[0]); }) + .attr("height", mini_yScale.rangeBand()) + .style("fill", "url(#gradient-mini)"); + + //EXIT + mini_bar.exit() + .remove(); + + //Start the brush + //gBrush.call(brush.event); + gBrush.call(brush.event); + prevRightClick = true; + } //Function runs on a brush move - to update the big bar chart function updateBarChart() { @@ -1790,7 +1879,7 @@ function brushmove() { //Create a gradient function createGradient(idName, endPerc) { - var colorsBarChart = ['#67001f','#b2182b','#d6604d','#f4a582','#fddbc7','#d1e5f0','#92c5de','#4393c3','#2166ac','#053061']; + var colorsBarChart = ['#000000']; colorsBarChart.reverse(); @@ -1806,7 +1895,7 @@ function createGradient(idName, endPerc) { .attr("stop-color", function(d) { return d; }); }//createGradient -function mapOrder(array, order, key) { +function mapOrder(array, order, key) { // Order an array according to a key. array.sort( function (a, b) { var A = a[key], B = b[key]; @@ -1817,8 +1906,8 @@ function mapOrder(array, order, key) { } }); - return array; + }; /** @@ -1913,43 +2002,10 @@ function mapOrder(array, order, key) { dy = p.y - point[1]; return dx * dx + dy * dy; } - - - -// Points are represented as objects with x and y attributes. - - - - /* var svg = d3.select('#modtSNEcanvas_svgClick').append('svg') - .attr('width', width) - .attr('height', height) - .on('mousemove', function() { - x = d3.event.pageX; - y = d3.event.pageY; - }); - - - var interactionSvgClick = d3.select("#modtSNEcanvas_svgClick").append("circle") - .attr("transform", "translate(" + x + "," + y + ")") - .attr("r", "3") - .attr("class", "dot") - .style('position', 'absolute') - .style("cursor", "pointer");*/ - //.call(drag); -} -/* -// Define drag behavior -var drag = d3.drag() - .on("drag", dragmove); - -function dragmove(d) { - var x = d3.event.x; - var y = d3.event.y; - d3.select(this).attr("transform", "translate(" + x + "," + y + ")"); } -*/ -function abbreviateNumber(value) { +function abbreviateNumber(value) { // Abbreviate the numbers for the main visualization legend! + var newValue = value; if (value >= 1000) { var suffixes = ["", "k", "m", "b","t"]; @@ -1964,9 +2020,11 @@ function abbreviateNumber(value) { newValue = shortValue+suffixes[suffixNum]; } return newValue; + } -function clearThree(obj){ +function clearThree(obj){ // Clear three.js object! + while(obj.children.length > 0){ clearThree(obj.children[0]) obj.remove(obj.children[0]); @@ -1974,50 +2032,52 @@ function clearThree(obj){ if(obj.geometry) obj.geometry.dispose() if(obj.material) obj.material.dispose() if(obj.texture) obj.texture.dispose() + } -var viewport3 = getViewport(); -var vw3 = viewport3[0] * 0.2; -var margin = {top: 40, right: 100, bottom: 40, left: 190}, +var viewport3 = getViewport(); // Get the width and height of the main visualization +var vw3 = viewport3[0] * 0.2; + +var margin = {top: 40, right: 100, bottom: 40, left: 190}, // Set the margins for the starplot width = Math.min(vw3, window.innerWidth - 10) - margin.left - margin.right, height = Math.min(width, window.innerHeight - margin.top - margin.bottom); - var wrapData = []; +var wrapData = []; +var radarChartOptions = { // starplot options + w: width, + h: height, + margin: margin, + maxValue: 100, + roundStrokes: true +}; +var colors; +var IDS = []; - ////////////////////////////////////////////////////////////// - //////////////////// Draw the Chart ////////////////////////// - ////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// +//////////////////// Draw the Chart ////////////////////////// +////////////////////////////////////////////////////////////// - - var radarChartOptions = { - w: width, - h: height, - margin: margin, - maxValue: 100, - roundStrokes: true - }; - var colors; - var IDS = []; - //Call function to draw the Radar chart - RadarChart("#starPlot", wrapData, colors, IDS, radarChartOptions); +//Call function to draw the Radar chart (starplot) +RadarChart("#starPlot", wrapData, colors, IDS, radarChartOptions); -function BetatSNE(points){ +function BetatSNE(points){ // Run the main visualization + +if (points.length) { // If points exist (at least 1 point) -if (points.length) { selectedPoints = []; var findNearestTable = []; for (let m=0; m1; k--){ + for (k=maxKNN; k>1; k--){ // Start from the maximum k value and go to the minimum (k=2). findNearest = 0; var indexOrderSliced = []; @@ -2063,14 +2125,14 @@ if (points.length) { counter1 = 0; counter2 = 0; - for (var i=0; i -1) { indices[i].splice(index, 1); } - // sorting the mapped array containing the reduced values + // Sorting the mapped array containing the reduced values indices[i].sort(function(a, b) { if (a[1] > b[1]) { return 1; @@ -2091,7 +2153,7 @@ if (points.length) { indexOrder[i] = indices[i].map(function(value) { return value[0]; }); - // temporary array holds objects with position and sort-value + // Temporary array holds objects with position and sort-value indices2d[i] = dists2d[i].map(function(el, i) { return [ i, el ]; }) @@ -2100,7 +2162,7 @@ if (points.length) { indices2d[i].splice(index2d, 1); } - // sorting the mapped array containing the reduced values + // Sorting the mapped array containing the reduced values indices2d[i].sort(function(a, b) { if (a[1] > b[1]) { return 1; @@ -2116,11 +2178,11 @@ if (points.length) { indexOrderSliced2d[i] = indexOrder2d[i].slice(0,k); for (var m=0; m < indexOrderSliced2d[i].length; m++){ - if (indexOrderSliced[i].includes(indexOrderSliced2d[i][m])){ + if (indexOrderSliced[i].includes(indexOrderSliced2d[i][m])){ // Union count1[i] = count1[i] + 1; temp[i] = temp[i] + 1; } - if(indexOrderSliced[i][m] == indexOrderSliced2d[i][m]){ + if(indexOrderSliced[i][m] == indexOrderSliced2d[i][m]){ // Intersection count2[i] = count2[i] + 1; temp2[i] = temp2[i] + 1; @@ -2136,31 +2198,31 @@ if (points.length) { } - sumUnion = counter1 / selectedPoints.length; - sumIntersection = counter2 / selectedPoints.length; + sumUnion = counter1 / selectedPoints.length; // Union + sumIntersection = counter2 / selectedPoints.length; // Intersection if (sumUnion == 0){ - findNearest = 0; + findNearest = 0; // Prevent the division by 0 value. } else{ - findNearest = sumIntersection / sumUnion; + findNearest = sumIntersection / sumUnion; // Nearest neighbor! } if (isNaN(findNearest)){ - findNearest = 0; + findNearest = 0; // If kNN is fully uncorrelated then we say that the value is 0. } - findNearestTable.push(findNearest * vh * 2); + findNearestTable.push(findNearest * vh * 2); // These values are multiplied by the height of the viewport because we need to draw the bins of the barchart representation } findNearestTable.reverse(); - var barPadding = 5; + var barPadding = 5; // Leave some space between the bars d3v3.select("#knnBarChart").selectAll("rect").remove(); - var svg2 = d3v3.select('#knnBarChart') + var svg2 = d3v3.select('#knnBarChart') // Create the barchart for the kNN .attr("class", "bar-chart"); - var barWidth = (vw / findNearestTable.length); + var barWidth = (vw / findNearestTable.length); // Bar width. - var knnBarChartSVG = svg2.selectAll("rect") + var knnBarChartSVG = svg2.selectAll("rect") // Draw the barchart! .data(findNearestTable) .enter() .append("rect") @@ -2176,20 +2238,21 @@ if (points.length) { return "translate("+ translate +")"; }); } + // Here we have the code for the starplot + d3.select("#starPlot").selectAll('g').remove(); // Remove the starplot if there was one before - d3.select("#starPlot").selectAll('g').remove(); var coun = 0; for (var i=0; i < selectedPoints.length; i++){ - if (selectedPoints[i].starplot == true){ + if (selectedPoints[i].starplot == true){ // Count the selected points coun = coun + 1; } } - if(selectedPoints.length <= 10 && coun > 0){ + if(selectedPoints.length <= 10 && coun > 0){ // If points > 10 then do not draw! If points = 0 then do not draw! var FeatureWise = []; - for (var j=0; j PCASelVec[b] ? 1 : 0; }); - //const list = dataFeatures.sort((a,b) => a.index - b.index).map((dataFeatures, index, array) => dataFeatures[Category]) + indices = indices.sort(function (a, b) { return PCASelVec[a] < PCASelVec[b] ? -1 : PCASelVec[a] > PCASelVec[b] ? 1 : 0; }); // Get the most important features first! Clockwise ordering var wrapData = []; var IDS = []; @@ -2238,41 +2301,41 @@ if (points.length) { if (!isNaN(Object.values(dataFeatures[selectedPoints[i].id])[j])){ for (m=0; m < len; m++){ if (indices[m] == j){ - if (Object.keys(dataFeatures[selectedPoints[i].id])[m] == Category) { + if (Object.keys(dataFeatures[selectedPoints[i].id])[m] == Category) { // Do not take into consideration the category whhich classifies the data. } else{ - data.push({axis:Object.keys(dataFeatures[selectedPoints[i].id])[m],value:Math.abs((Object.values(dataFeatures[selectedPoints[i].id])[m] - min[m])/(max[m] - min[m]))}); + data.push({axis:Object.keys(dataFeatures[selectedPoints[i].id])[m],value:Math.abs((Object.values(dataFeatures[selectedPoints[i].id])[m] - min[m])/(max[m] - min[m]))}); // Push the values into the starplot } } } } } - wrapData.push(data); - IDS.push(selectedPoints[i].id); + wrapData.push(data); // Wrap everything together + IDS.push(selectedPoints[i].id); // Push all the IDs of the selected points } ////////////////////////////////////////////////////////////// //////////////////// Draw the Chart ////////////////////////// ////////////////////////////////////////////////////////////// - var colors = ['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462','#b3de69','#fccde5','#d9d9d9','#bc80bd']; + var colors = ['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462','#b3de69','#fccde5','#d9d9d9','#bc80bd']; // Colorscale for the starplot var colorScl = d3v3.scale.ordinal() .domain(IDS) .range(colors); - var radarChartOptions = { + var radarChartOptions = { // Starplot options w: width, h: height, margin: margin, levels: 10, roundStrokes: true, }; - //Call function to draw the Radar chart + + //Call function to draw the Radar chart (starplot) RadarChart("#starPlot", wrapData, colorScl, IDS, radarChartOptions); } - - var ColSizeSelector = document.getElementById("param-neighborHood").value; + var ColSizeSelector = document.getElementById("param-neighborHood").value; // This is the mapping of the color/size in beta/KLD - if (ColSizeSelector == "color") { + if (ColSizeSelector == "color") { // If we have beta into color then calculate the color scales var max = (d3.max(points,function(d){ return d.beta; })); var min = (d3.min(points,function(d){ return d.beta; })); // colors @@ -2291,7 +2354,7 @@ if (points.length) { var colorScale = d3.scaleLinear() .domain(d3.range(0, max+calcStep, calcStep)) .range(colorbrewer); - points = points.sort(function(a, b) { + points = points.sort(function(a, b) { // Sort them according to importance (darker color!) return a.beta - b.beta; }) var labels_beta = []; @@ -2316,23 +2379,23 @@ if (points.length) { svg.select(".legendLinear") .call(legend); - } else { - var max = (d3.max(points,function(d){ return d.cost; })); - var min = (d3.min(points,function(d){ return d.cost; })); - - var maxSize2 = (d3.max(points,function(d){ return d.beta; })); - var minSize2 = (d3.min(points,function(d){ return d.beta; })); - var rscale2 = d3.scaleLinear() - .domain([minSize2, maxSize2]) - .range([5,12]); + } else { // If we have cost into color then calculate the color scales + var max = (d3.max(points,function(d){ return d.cost; })); + var min = (d3.min(points,function(d){ return d.cost; })); + + var maxSize2 = (d3.max(points,function(d){ return d.beta; })); + var minSize2 = (d3.min(points,function(d){ return d.beta; })); + var rscale2 = d3.scaleLinear() + .domain([minSize2, maxSize2]) + .range([5,12]); - var colorbrewer = ["#ffffe5","#f7fcb9","#d9f0a3","#addd8e","#78c679","#41ab5d","#238443","#006837","#004529"]; - var calcStep = (max-min)/9; - var colorScale = d3.scaleLinear() - .domain(d3.range(min, max, calcStep)) - .range(colorbrewer); + var colorbrewer = ["#ffffe5","#f7fcb9","#d9f0a3","#addd8e","#78c679","#41ab5d","#238443","#006837","#004529"]; + var calcStep = (max-min)/9; + var colorScale = d3.scaleLinear() + .domain(d3.range(min, max, calcStep)) + .range(colorbrewer); - points = points.sort(function(a, b) { + points = points.sort(function(a, b) { // Sort them according to importance (darker color!) return a.cost - b.cost; }) @@ -2344,7 +2407,7 @@ if (points.length) { abbr_labels_cost[i] = abbreviateNumber(labels_cost[i]); } - var svg = d3.select("#legend1"); + var svg = d3.select("#legend1"); // Add the legend for the beta/cost svg.append("g") .attr("class", "legendLinear") @@ -2361,6 +2424,22 @@ if (points.length) { .call(legend); } + + let tempSort = -1; + + for (var i=0; i { dimensions = window.innerWidth; dimensions = window.innerHeight; @@ -2387,17 +2466,13 @@ if (points.length) { camera.position.set(0, 0, far); } - //if(step_counter == max_counter){ setUpZoom(); - //} - var circle_sprite= new THREE.TextureLoader().load( + var circle_sprite= new THREE.TextureLoader().load( // Add the circle effect "./textures/circle-sprite.png" ) - - - clearThree(scene); + clearThree(scene); // Clear previous scenes // Increase/reduce size factor selected by the user var limitdist = document.getElementById("param-lim-value").value; @@ -2463,93 +2538,79 @@ if (points.length) { scene.add(particles); } - let tempSort = -1; - - for (var i=0; i