// t-SNE.js object and other global variables 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; var prevRightClick; // 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", 1); } var ringNotes = []; var gAnnotationsAll = []; var AnnotationsAll = []; var draggable = []; function setReset(){ d3.selectAll("#correlation > *").remove(); d3.selectAll("#modtSNEcanvas_svg > *").remove(); lassoEnable(); flag = false; d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); d3.selectAll("#SvgAnnotator > *").remove(); 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); } function setReInitialize(){ 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); d3.select("#modtSNEcanvas_svg_Schema").style("z-index", 1); } function setLayerComp(){ d3.select("#modtSNEcanvas_svg").style("z-index", 2); d3.select("#modtSNEcanvas_svg_Schema").style("z-index", 1); d3.select("#modtSNEcanvas").style("z-index", 1); lassoEnable(); } function setLayerSche(){ d3.select("#modtSNEcanvas_svg_Schema").style("z-index", 2); d3.select("#modtSNEcanvas").style("z-index", 1); d3.select("#modtSNEcanvas_svg").style("z-index", 1); click(); } function lassoEnable(){ var interactionSvg = d3.select("#modtSNEcanvas_svg") .attr("width", dimensions) .attr("height", dimensions) .style('position', 'absolute') .style('top', 0) .style('left', 0); var lassoInstance = lasso() .on('end', handleLassoEnd) .on('start', handleLassoStart); interactionSvg.call(lassoInstance); } 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; 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(); // function that executes after data is successfully loaded function init(data, results_all, fields) { prevRightClick = false; 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 () { d3.selectAll("#sheparheat > *").remove(); 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){ d3.selectAll("#correlation > *").remove(); d3.selectAll("#modtSNEcanvas_svg > *").remove(); lassoEnable(); d3.selectAll("#modtSNEcanvas_svg_Schema > *").remove(); d3.selectAll("#SvgAnnotator > *").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)); } } } ArrayLimit = []; 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); for (let k = 0; k < dataFeatures.length; k++){ ArrayContainsDataFeatures.push(Object.values(dataFeatures[k]).concat(k)); } ArrayContainsDataFeatures = mapOrder(ArrayContainsDataFeatures, Order, 5); ArrayContainsDataFeaturesLimit = []; 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); } var SignStore = []; correlationResults = []; 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); } if (SignStore[i][1] == correlationResults[j][1]) { correlationResults[j][1] = parseInt(correlationResults[j][1] * 100); correlationResults[j].push(j); } } } } drawBarChart(); } } function drawBarChart(){ d3.selectAll("#correlation > *").remove(); ///////////////////////////////////////////////////////////// ///////////////// 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); prevRightClick = true; } //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: 40, right: 100, bottom: 40, left: 190}, width = Math.min(vw3, window.innerWidth - 10) - margin.left - margin.right, height = Math.min(width, window.innerHeight - margin.top - margin.bottom); var wrapData = []; ////////////////////////////////////////////////////////////// //////////////////// 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); 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; 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(dimensions/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 / dimensions * 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