t-viSNE: Interactive Assessment and Interpretation of t-SNE Projections
https://doi.org/10.1109/TVCG.2020.2986996
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2866 lines
87 KiB
2866 lines
87 KiB
// 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<dists2d.length; j++){
|
|
dists2d[j] = dists2d[j].slice(0,j);
|
|
dists[j] = dists[j].slice(0,j);
|
|
}
|
|
|
|
for (var i=0; i<dists2d.length; i++){
|
|
for (var j=0; j<dists2d.length; j++){
|
|
let singleObj = {};
|
|
singleObj = dists2d[i][j];
|
|
dist_list2d.push(singleObj);
|
|
let singleObj2 = {};
|
|
singleObj2 = dists[i][j];
|
|
dist_list.push(singleObj2);
|
|
}
|
|
}
|
|
dist_list2d = dist_list2d.sort();
|
|
dist_list = dist_list.sort();
|
|
dist_list2d = dist_list2d.filter(function(val){ return val!==undefined; });
|
|
dist_list = dist_list.filter(function(val){ return val!==undefined; });
|
|
d3.tsv("./modules/heat.tsv").then(function(data) {
|
|
|
|
data.forEach(function(d) {
|
|
d.dim1 = +d.dim1;
|
|
d.dim2 = +d.dim2;
|
|
d.value = 0;
|
|
});
|
|
|
|
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];
|
|
for (var l=0; l<100; l++){
|
|
for (k=0; k<dist_list_all[0].length;k++){
|
|
if (l==0){
|
|
if (dist_list_all[0][k] <= data[l].dim1/10 && dist_list_all[1][k] <= data[l].dim2/10){
|
|
counnum[l] = counnum[l] + 1;
|
|
}
|
|
}else if (l <= 10){
|
|
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){
|
|
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<data.length; m++)
|
|
{
|
|
data[m].value = counnum[m];
|
|
}
|
|
|
|
var maxNum = Math.round(d3.max(data,function(d){ return d.value; }));
|
|
var minNum = Math.round(d3.min(data,function(d){ return d.value; }));
|
|
var colors = ["#f7fbff","#deebf7","#c6dbef","#9ecae1","#6baed6","#4292c6","#2171b5","#08519c","#08306b"];
|
|
let calcStep = (maxNum-minNum)/colors.length;
|
|
var colorScale = d3.scaleLinear()
|
|
.domain(d3.range(0, maxNum+calcStep,calcStep))
|
|
.range(colors);
|
|
var tip = d3.tip()
|
|
.attr('class', 'd3-tip')
|
|
.style("visibility","visible")
|
|
.offset([-20, 0])
|
|
.html(function(d) {
|
|
return "Value: <span style='color:red'>" + 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; i<points.length; i++){
|
|
let singleObj = {};
|
|
// convert the position from pixels to 0.0 to 1.0
|
|
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<points.length; i++){
|
|
let singleCol = {};
|
|
if (points[i].selected == false){
|
|
let colval = d3.rgb(211,211,211);
|
|
singleCol = colval.r/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.g/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.b/255;
|
|
colors.push(singleCol);
|
|
}else{
|
|
let colval = d3.rgb(colorScale(points[i].name));
|
|
singleCol = colval.r/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.g/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.b/255;
|
|
colors.push(singleCol);
|
|
}
|
|
}
|
|
// Create an empty buffer object and store vertex data
|
|
var vertex_buffer = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
|
|
// Create an empty buffer object and store color data
|
|
var color_buffer = gl.createBuffer ();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
|
|
|
|
// vertex shader source code
|
|
var vertCode = 'attribute vec3 coordinates;'+
|
|
'attribute vec3 color;'+
|
|
'varying vec3 vColor;'+
|
|
'void main(void) {' +
|
|
' gl_Position = vec4(coordinates, 1.0);' +
|
|
'vColor = color;'+
|
|
'gl_PointSize = 3.5;'+
|
|
'}';
|
|
|
|
// Create a vertex shader object
|
|
var vertShader = gl.createShader(gl.VERTEX_SHADER);
|
|
|
|
// Attach vertex shader source code
|
|
gl.shaderSource(vertShader, vertCode);
|
|
|
|
// Compile the vertex shader
|
|
gl.compileShader(vertShader);
|
|
|
|
var fragCode = `
|
|
#ifdef GL_OES_standard_derivatives
|
|
#extension GL_OES_standard_derivatives : enable
|
|
#endif
|
|
|
|
precision mediump float;
|
|
varying vec3 vColor;
|
|
|
|
void main()
|
|
{
|
|
float r = 0.0, delta = 0.0, alpha = 1.0;
|
|
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
|
|
r = dot(cxy, cxy);
|
|
#ifdef GL_OES_standard_derivatives
|
|
delta = fwidth(r);
|
|
alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r);
|
|
#endif
|
|
|
|
gl_FragColor = vec4(vColor, alpha);
|
|
}`;
|
|
gl.getExtension('OES_standard_derivatives');
|
|
// Create fragment shader object
|
|
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
|
|
|
|
// Attach fragment shader source code
|
|
gl.shaderSource(fragShader, fragCode);
|
|
|
|
// Compile the fragmentt shader
|
|
gl.compileShader(fragShader);
|
|
|
|
// Create a shader program object to
|
|
// store the combined shader program
|
|
var shaderProgram = gl.createProgram();
|
|
|
|
// Attach a vertex shader
|
|
gl.attachShader(shaderProgram, vertShader);
|
|
|
|
// Attach a fragment shader
|
|
gl.attachShader(shaderProgram, fragShader);
|
|
|
|
// Link both the programs
|
|
gl.linkProgram(shaderProgram);
|
|
|
|
// Use the combined shader program object
|
|
gl.useProgram(shaderProgram);
|
|
|
|
|
|
|
|
// Bind vertex buffer object
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
|
|
|
|
// Get the attribute location
|
|
var coord = gl.getAttribLocation(shaderProgram, "coordinates");
|
|
|
|
// point an attribute to the currently bound VBO
|
|
gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
|
|
|
|
// Enable the attribute
|
|
gl.enableVertexAttribArray(coord);
|
|
|
|
// bind the color buffer
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
|
|
|
|
// get the attribute location
|
|
var color = gl.getAttribLocation(shaderProgram, "color");
|
|
|
|
// point attribute to the volor buffer object
|
|
gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ;
|
|
|
|
// enable the color attribute
|
|
gl.enableVertexAttribArray(color);
|
|
|
|
|
|
// Clear the canvas
|
|
gl.clearColor(1.0, 1.0, 1.0, 1.0);
|
|
|
|
// Enable the depth test
|
|
gl.disable(gl.DEPTH_TEST);
|
|
|
|
// Clear the color buffer bit
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
|
|
resize(gl.canvas);
|
|
|
|
gl.viewport(0, 0, dim, dim);
|
|
|
|
//Draw the triangle
|
|
gl.drawArrays(gl.POINTS, 0, points.length);
|
|
|
|
}
|
|
|
|
function redraw(repoints){
|
|
// OverviewtSNE(repoints);
|
|
BetatSNE(repoints);
|
|
}
|
|
|
|
function handleLassoEnd(lassoPolygon) {
|
|
var countLassoFalse = 0;
|
|
KNNEnabled = true;
|
|
for (var i = 0 ; i < points.length ; i ++) {
|
|
x = points[i].x;
|
|
y = points[i].y;
|
|
if (d3.polygonContains(lassoPolygon, [x, y])){
|
|
points[i].selected = true;
|
|
points2d[i].selected = true;
|
|
} else{
|
|
countLassoFalse = countLassoFalse + 1;
|
|
points[i].selected = false;
|
|
points2d[i].selected = true;
|
|
}
|
|
}
|
|
if (countLassoFalse == points.length){
|
|
for (var i = 0 ; i < points.length ; i ++) {
|
|
points[i].selected = true;
|
|
points2d[i].selected = true;
|
|
}
|
|
}
|
|
if (points.length - countLassoFalse <= 10 && points.length - countLassoFalse != 0){
|
|
for (var i = 0 ; i < points.length ; i ++) {
|
|
if (points[i].selected == true){
|
|
points[i].starplot = true;
|
|
}
|
|
}
|
|
} else{
|
|
for (var i = 0 ; i < points.length ; i ++) {
|
|
points[i].starplot = false;
|
|
}
|
|
}
|
|
redraw(points);
|
|
|
|
}
|
|
|
|
// reset selected points when starting a new polygon
|
|
function handleLassoStart(lassoPolygon) {
|
|
|
|
KNNEnabled = false;
|
|
for (var i = 0 ; i < points.length ; i ++) {
|
|
points[i].selected = true;
|
|
points[i].starplot = false;
|
|
points2d[i].selected = true;
|
|
}
|
|
|
|
redraw(points);
|
|
}
|
|
|
|
|
|
var svg,
|
|
defs,
|
|
gBrush,
|
|
brush,
|
|
main_xScale,
|
|
mini_xScale,
|
|
main_yScale,
|
|
mini_yScale,
|
|
main_xAxis,
|
|
main_yAxis,
|
|
mini_width,
|
|
textScale;
|
|
|
|
//Added only for the mouse wheel
|
|
var zoomer = d3v3.behavior.zoom()
|
|
.on("zoom", null);
|
|
|
|
var main_margin = {top: 8, right: 10, bottom: 30, left: 100},
|
|
main_width = 500 - main_margin.left - main_margin.right,
|
|
main_height = 320 - main_margin.top - main_margin.bottom;
|
|
|
|
var mini_margin = {top: 8, right: 10, bottom: 30, left: 10},
|
|
mini_height = 320 - mini_margin.top - mini_margin.bottom;
|
|
mini_width = 100 - mini_margin.left - mini_margin.right;
|
|
|
|
svg = d3v3.select("#correlation").attr("class", "svgWrapper")
|
|
.attr("width", main_width + main_margin.left + main_margin.right + mini_width + mini_margin.left + mini_margin.right)
|
|
.attr("height", main_height + main_margin.top + main_margin.bottom)
|
|
.call(zoomer)
|
|
.on("wheel.zoom", scroll)
|
|
.on("mousedown.zoom", null)
|
|
.on("touchstart.zoom", null)
|
|
.on("touchmove.zoom", null)
|
|
.on("touchend.zoom", null);
|
|
|
|
var svgClick;
|
|
var flag = false;
|
|
|
|
function click(){
|
|
|
|
svgClick = d3.select('#modtSNEcanvas_svg_Schema');
|
|
|
|
function drawCircle(x, y, size) {
|
|
svgClick.append("circle")
|
|
.attr('class', 'click-circle')
|
|
.attr("cx", x)
|
|
.attr("cy", y)
|
|
.attr("r", size);
|
|
Arrayx.push(x);
|
|
Arrayy.push(y);
|
|
}
|
|
svgClick.on('click', function() {
|
|
if (prevRightClick == false){
|
|
var coords = d3.mouse(this);
|
|
drawCircle(coords[0], coords[1], 3);
|
|
}
|
|
for (var k = 0; k < Arrayx.length ; k++){
|
|
Arrayxy[k] = [Arrayx[k], Arrayy[k]];
|
|
}
|
|
|
|
for (var k = 0; k < Arrayxy.length - 1 ; k++){
|
|
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);
|
|
}
|
|
});
|
|
|
|
svgClick.on("contextmenu", function (d) {
|
|
|
|
if (prevRightClick == true){
|
|
|
|
} else {
|
|
|
|
var line = d3.line().curve(d3.curveCardinal);
|
|
|
|
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();
|
|
|
|
flag = true;
|
|
CalculateCorrel();
|
|
}
|
|
});
|
|
}
|
|
|
|
function CalculateCorrel(){
|
|
|
|
if (flag == false){
|
|
alert("Please, draw a schema first!");
|
|
} else{
|
|
var correlLimit = document.getElementById("param-corr-value").value;
|
|
correlLimit = parseInt(correlLimit);
|
|
|
|
allTransformPoints = [];
|
|
for (var loop = 0; loop < points.length ; loop++){
|
|
allTransformPoints[loop] = [points[loop].x, points[loop].y, points[loop].id, points[loop].beta, points[loop].cost, points[loop].selected];
|
|
}
|
|
|
|
var line = svgClick.append("line");
|
|
|
|
paths = svgClick.selectAll("path").filter(".SchemaCheck");
|
|
XYDistId = [];
|
|
if (paths.nodes().length == 0){
|
|
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++){
|
|
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 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<arraysCleared.length; i++) {
|
|
if (arraysCleared[i][5] < correlLimit) {
|
|
ArrayLimit.push(arraysCleared[i]);
|
|
}
|
|
}
|
|
|
|
var temparray = [];
|
|
var count = new Array(paths.nodes().length).fill(0);
|
|
for (var m=0; m < paths.nodes().length; m++) {
|
|
for (var i=0; i<ArrayLimit.length; i++) {
|
|
if (ArrayLimit[i][2] == m){
|
|
count[m] = count[m] + 1;
|
|
temparray.push(ArrayLimit[i]);
|
|
}
|
|
// do whatever
|
|
}
|
|
}
|
|
var arraysSplitted = [];
|
|
for (var m=0; m < paths.nodes().length; m++) {
|
|
arraysSplitted.push(temparray.splice(0, count[m]));
|
|
}
|
|
|
|
|
|
|
|
for (var m=0; m < paths.nodes().length; m++) {
|
|
arraysSplitted[m] = arraysSplitted[m].sort(function(a, b){
|
|
var dist = (a[0]-a[3]) * (a[0]-a[3]) + (a[1]-a[4]) * (a[1]-a[4]);
|
|
var distAgain = (b[0]-b[3]) * (b[0]-b[3]) + (b[1]-b[4]) * (b[1]-b[4]);
|
|
// Compare the 2 dates
|
|
if(dist < distAgain) return -1;
|
|
if(distAgain > 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; m<points.length; m++){
|
|
if (points[m].selected == true){
|
|
selectedPoints.push(points[m]);
|
|
}
|
|
}
|
|
if (KNNEnabled == true && selectedPoints.length != 0){
|
|
var distsFull = dists;
|
|
var dists2dFull = dists2d;
|
|
|
|
for (var i=0; i<dists.length; i++){
|
|
for (var j=0; j<dists.length; j++){
|
|
if(dists[i][j] != null) {
|
|
distsFull[j][i] = dists[i][j];
|
|
dists2dFull[j][i] = dists2d[i][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
var indexOrder = [];
|
|
var indexOrder2d = [];
|
|
var indices = new Array(selectedPoints.length);
|
|
var indices2d = new Array(selectedPoints.length);
|
|
|
|
var findNearest;
|
|
var counter1;
|
|
var counter2;
|
|
|
|
var temp = [];
|
|
var temp2 = [];
|
|
|
|
var viewport = getViewport();
|
|
var vw = viewport[0] * 0.5;
|
|
var vh = viewport[1] * 0.042;
|
|
var factor = Math.log10(points.length) * 4;
|
|
if (factor == 0){
|
|
factor = 1;
|
|
}
|
|
var maxKNN = Math.ceil(points.length / factor);
|
|
|
|
selectedPoints.sort(function(a, b) {
|
|
return parseFloat(a.id) - parseFloat(b.id);
|
|
});
|
|
|
|
$("#kNNDetails").html("Purity of the cluster was checked for k values starting from " + (1) + " to " + maxKNN + ".");
|
|
|
|
for (k=maxKNN; k>0; 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<selectedPoints.length; i++){
|
|
|
|
temp[i] = 0;
|
|
temp2[i] = 0;
|
|
|
|
if (k == maxKNN){
|
|
|
|
|
|
// temporary array holds objects with position and sort-value
|
|
indices[i] = dists[i].map(function(el, i) {
|
|
return [ i, el ];
|
|
})
|
|
var index = indices[i].indexOf(selectedPoints[i].id);
|
|
if (index > -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<Object.values(dataFeatures[0]).length; j++){
|
|
for (var i=0;i<dataFeatures.length;i++){
|
|
if (!isNaN(Object.values(dataFeatures[i])[j])){
|
|
FeatureWise.push(Object.values(dataFeatures[i])[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
var sum = new Array(Object.values(dataFeatures[0]).length).fill(0);
|
|
|
|
for (var j=0; j<Object.values(dataFeatures[0]).length; j++){
|
|
var FeatureWiseSliced = FeatureWise.slice(0+(j*dataFeatures.length),dataFeatures.length+j*dataFeatures.length);
|
|
for (var i=0; i<FeatureWiseSliced.length; i++){
|
|
sum[j] = FeatureWiseSliced[i] + sum[j];
|
|
}
|
|
}
|
|
|
|
var wrapData = [];
|
|
var IDS = [];
|
|
for (var i=0; i<selectedPoints.length; i++){
|
|
var data = [];
|
|
for (var j=0; j< Object.keys(dataFeatures[selectedPoints[i].id]).length; j++){
|
|
if (!isNaN(Object.values(dataFeatures[i])[j])){
|
|
if (Object.keys(dataFeatures[i])[j] == "name") {
|
|
|
|
} else{
|
|
data.push({axis:Object.keys(dataFeatures[i])[j],value:Math.abs(Object.values(dataFeatures[i])[j]*100/sum[j])});
|
|
}
|
|
}
|
|
}
|
|
wrapData.push(data);
|
|
IDS.push(selectedPoints[i].id);
|
|
}
|
|
//////////////////////////////////////////////////////////////
|
|
//////////////////// Draw the Chart //////////////////////////
|
|
//////////////////////////////////////////////////////////////
|
|
var colors = ['#8dd3c7','#ffffb3','#bebada','#fb8072','#80b1d3','#fdb462','#b3de69','#fccde5','#d9d9d9','#bc80bd'];
|
|
var colorScl = d3v3.scale.ordinal()
|
|
.domain(IDS)
|
|
.range(colors);
|
|
|
|
var radarChartOptions = {
|
|
w: width,
|
|
h: height,
|
|
margin: margin,
|
|
levels: 10,
|
|
roundStrokes: true,
|
|
};
|
|
//Call function to draw the Radar chart
|
|
RadarChart("#starPlot", wrapData, colorScl, IDS, radarChartOptions);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
window.addEventListener('resize', () => {
|
|
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<points.length; i++) {
|
|
|
|
let vertex = new THREE.Vector3((((points[i].x/dimensions)*2) - 1)*dimensions, (((points[i].y/dimensions)*2) - 1)*dimensions*-1, 0);
|
|
pointsGeometry.vertices.push(vertex);
|
|
|
|
if (points[i].selected == false){
|
|
var color = new THREE.Color("rgb(211, 211, 211)");
|
|
} else if (points[i].DimON != null) {
|
|
|
|
let temp = points[i].DimON.match(/\d+/)[0];
|
|
|
|
var maxDim = (d3.max(points,function(d){ if(d.selected == true){return d[temp]}; }));
|
|
var minDim = (d3.min(points,function(d){ if(d.selected == true){return d[temp]}; }));
|
|
|
|
let colorsBarChart = ['#fcfbfd','#efedf5','#dadaeb','#bcbddc','#9e9ac8','#807dba','#6a51a3','#54278f','#3f007d'];
|
|
var calcStepDim = (maxDim-minDim)/8;
|
|
var colorScale = d3.scaleLinear()
|
|
.domain(d3.range(minDim, maxDim+calcStepDim, calcStepDim))
|
|
.range(colorsBarChart);
|
|
var color = new THREE.Color(colorScale(points[i][temp]));
|
|
|
|
} else if(points[i].starplot == true){
|
|
var color = new THREE.Color(colorScl(points[i].id));
|
|
} else if (ColSizeSelector == "color") {
|
|
var color = new THREE.Color(colorScale(points[i].beta));
|
|
}
|
|
else{
|
|
var color = new THREE.Color(colorScale(points[i].cost));
|
|
}
|
|
if (ColSizeSelector == "color") {
|
|
let sizePoint = rscale1(points[i].cost);
|
|
factorPlusSize[i] = limitdist * sizePoint;
|
|
pointsGeometry.colors.push(color);
|
|
pointsMaterial[i] = new THREE.PointsMaterial({
|
|
size: factorPlusSize[i],
|
|
sizeAttenuation: false,
|
|
vertexColors: THREE.VertexColors,
|
|
map: circle_sprite,
|
|
transparent: true
|
|
});
|
|
} else{
|
|
let sizePoint = rscale2(points[i].beta);
|
|
factorPlusSize[i] = limitdist * sizePoint;
|
|
pointsGeometry.colors.push(color);
|
|
pointsMaterial[i] = new THREE.PointsMaterial({
|
|
size: factorPlusSize[i],
|
|
sizeAttenuation: false,
|
|
vertexColors: THREE.VertexColors,
|
|
map: circle_sprite,
|
|
transparent: true
|
|
});
|
|
}
|
|
var particles = new THREE.Points(pointsGeometry, pointsMaterial[i]);
|
|
scene.add(particles);
|
|
}
|
|
|
|
let tempSort = -1;
|
|
|
|
for (var i=0; i<points.length; i++){
|
|
if (points[i].DimON != null) {
|
|
tempSort = points[i].DimON.match(/\d+/)[0];
|
|
}
|
|
}
|
|
|
|
if (tempSort != -1){
|
|
/*points = points.sort(function(a, b) {
|
|
return a[tempSort] - b[tempSort];
|
|
})*/
|
|
}
|
|
|
|
var temporal = 0;
|
|
for (var j=0; j < points.length; j++){
|
|
if(points[j].DimON != null) {
|
|
temporal = temporal + 1;
|
|
var labels_dim = [];
|
|
var abbr_labels_dim = [];
|
|
labels_dim = d3.range(minDim, maxDim+calcStepDim, calcStepDim);
|
|
|
|
for (var i=0; i<9; i++){
|
|
labels_dim[i] = labels_dim[i].toFixed(2);
|
|
abbr_labels_dim[i] = abbreviateNumber(labels_dim[i]);
|
|
}
|
|
d3.select("#legend1").selectAll('*').remove();
|
|
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_dim[0],abbr_labels_dim[1],abbr_labels_dim[2],abbr_labels_dim[3],abbr_labels_dim[4],abbr_labels_dim[5],abbr_labels_dim[6],abbr_labels_dim[7],abbr_labels_dim[8]])
|
|
.title(points[j].DimON)
|
|
.scale(colorScale);
|
|
|
|
svg.select(".legendLinear")
|
|
.call(legend);
|
|
break;
|
|
}
|
|
}
|
|
// This is for the legend
|
|
for (var j=0; j < points.length; j++){
|
|
if(temporal == 0 && points[j].DimON == null){
|
|
if (ColSizeSelector == "color"){
|
|
d3.select("#legend1").selectAll('*').remove();
|
|
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);
|
|
break;
|
|
} else {
|
|
d3.select("#legend1").selectAll('*').remove();
|
|
var svg = d3.select("#legend1");
|
|
|
|
svg.append("g")
|
|
.attr("class", "legendLinear")
|
|
.attr("transform", "translate(10,15)");
|
|
|
|
var legend = d3.legendColor()
|
|
.labelFormat(d3.format(".4f"))
|
|
.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);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function zoomHandler(d3_transform) {
|
|
let scale = d3_transform.k;
|
|
let x = -(d3_transform.x - dimensions/2) / scale;
|
|
let y = (d3_transform.y - dimensions/2) / scale;
|
|
let z = getZFromScale(scale);
|
|
camera.position.set(x, y, z);
|
|
}
|
|
|
|
function getScaleFromZ (camera_z_position) {
|
|
let half_fov = fov/2;
|
|
let half_fov_radians = toRadians(half_fov);
|
|
let half_fov_height = Math.tan(half_fov_radians) * camera_z_position;
|
|
let fov_height = half_fov_height * 2;
|
|
let scale = dimensions / fov_height; // Divide visualization height by height derived from field of view
|
|
return scale;
|
|
}
|
|
|
|
function getZFromScale(scale) {
|
|
let half_fov = fov/2;
|
|
let half_fov_radians = toRadians(half_fov);
|
|
let scale_height = dimensions / scale;
|
|
let camera_z_position = scale_height / (2 * Math.tan(half_fov_radians));
|
|
return camera_z_position;
|
|
}
|
|
|
|
function toRadians (angle) {
|
|
return angle * (Math.PI / 180);
|
|
}
|
|
|
|
// Hover and tooltip interaction
|
|
|
|
raycaster = new THREE.Raycaster();
|
|
raycaster.params.Points.threshold = 10;
|
|
|
|
view.on("mousemove", () => {
|
|
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(`<div id="tooltip" style="display: none; z-index: 2; position: absolute; pointer-events: none; font-size: 13px; width: 240px; text-align: center; line-height: 1; padding: 6px; background: white; font-family: sans-serif;">
|
|
<div id="point_tip" style="padding: 4px; margin-bottom: 4px;"></div>
|
|
<div id="group_tip" style="padding: 4px;"></div>
|
|
</div>`);
|
|
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; i<points.length; i++){
|
|
if (points[i].DimON != null) {
|
|
tempSort = points[i].DimON.match(/\d+/)[0];
|
|
}
|
|
}
|
|
|
|
if (tempSort != -1){
|
|
points = points.sort(function(a, b) {
|
|
return a[tempSort] - b[tempSort];
|
|
})
|
|
}
|
|
|
|
|
|
for (var i=0; i<points.length; i++){
|
|
let singleObj = {};
|
|
// convert the position from pixels to 0.0 to 1.0
|
|
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<points.length; i++){
|
|
let singleCol = {};
|
|
|
|
if (ColSizeSelector == "color"){
|
|
let singleSizeCol = rscale1(points[i].cost);
|
|
sizes[i] = singleSizeCol;
|
|
} else {
|
|
let singleSizeCol = rscale2(points[i].beta);
|
|
sizes[i] = singleSizeCol;
|
|
}
|
|
|
|
if (points[i].selected == false){
|
|
let colval = d3.rgb(211,211,211);
|
|
singleCol = colval.r/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.g/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.b/255;
|
|
colors.push(singleCol);
|
|
} else if (points[i].DimON != null) {
|
|
|
|
let temp = points[i].DimON.match(/\d+/)[0];
|
|
|
|
var maxDim = (d3.max(points,function(d){ if(d.selected == true){return d[temp]}; }));
|
|
var minDim = (d3.min(points,function(d){ if(d.selected == true){return d[temp]}; }));
|
|
|
|
let colorsBarChart = ['#fcfbfd','#efedf5','#dadaeb','#bcbddc','#9e9ac8','#807dba','#6a51a3','#54278f','#3f007d'];
|
|
var calcStepDim = (maxDim-minDim)/8;
|
|
var colorScaleDim = d3.scaleLinear()
|
|
.domain(d3.range(minDim, maxDim+calcStepDim, calcStepDim))
|
|
.range(colorsBarChart);
|
|
|
|
let colval = d3.rgb(colorScaleDim(points[i][temp]));
|
|
singleCol = colval.r/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.g/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.b/255;
|
|
colors.push(singleCol);
|
|
|
|
} else if (ColSizeSelector == "color") {
|
|
let colval = d3.rgb(colorScale(points[i].beta));
|
|
singleCol = colval.r/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.g/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.b/255;
|
|
colors.push(singleCol);
|
|
}
|
|
else{
|
|
let colval = d3.rgb(colorScale(points[i].cost));
|
|
singleCol = colval.r/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.g/255;
|
|
colors.push(singleCol);
|
|
singleCol = colval.b/255;
|
|
colors.push(singleCol);
|
|
}
|
|
}
|
|
|
|
var temporal = 0;
|
|
for (var j=0; j < points.length; j++){
|
|
if(points[j].DimON != null) {
|
|
temporal = temporal + 1;
|
|
var labels_dim = [];
|
|
var abbr_labels_dim = [];
|
|
labels_dim = d3.range(minDim, maxDim+calcStepDim, calcStepDim);
|
|
|
|
for (var i=0; i<9; i++){
|
|
labels_dim[i] = labels_dim[i].toFixed(2);
|
|
abbr_labels_dim[i] = abbreviateNumber(labels_dim[i]);
|
|
}
|
|
d3.select("#legend1").selectAll('*').remove();
|
|
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_dim[0],abbr_labels_dim[1],abbr_labels_dim[2],abbr_labels_dim[3],abbr_labels_dim[4],abbr_labels_dim[5],abbr_labels_dim[6],abbr_labels_dim[7],abbr_labels_dim[8]])
|
|
.title(points[j].DimON)
|
|
.scale(colorScaleDim);
|
|
|
|
svg.select(".legendLinear")
|
|
.call(legend);
|
|
break;
|
|
}
|
|
}
|
|
for (var j=0; j < points.length; j++){
|
|
if(temporal == 0 && points[j].DimON == null){
|
|
if (ColSizeSelector == "color"){
|
|
d3.select("#legend1").selectAll('*').remove();
|
|
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);
|
|
break;
|
|
} else {
|
|
d3.select("#legend1").selectAll('*').remove();
|
|
var svg = d3.select("#legend1");
|
|
|
|
svg.append("g")
|
|
.attr("class", "legendLinear")
|
|
.attr("transform", "translate(10,15)");
|
|
|
|
var legend = d3.legendColor()
|
|
.labelFormat(d3.format(".4f"))
|
|
.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);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Create an empty buffer object and store vertex data
|
|
var vertex_buffer = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
|
|
// Create an empty buffer object and store color data
|
|
var color_buffer = gl.createBuffer ();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
|
|
|
|
var size_buffer = gl.createBuffer();
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, size_buffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, sizes, gl.STATIC_DRAW);
|
|
|
|
// Increase/reduce size factor selected by the user
|
|
var limitdist = document.getElementById("param-lim-value").value;
|
|
limitdist = parseFloat(limitdist).toFixed(1);
|
|
|
|
// vertex shader source code
|
|
var vertCode = 'attribute vec3 coordinates;'+
|
|
'attribute float vSizes;'+
|
|
'attribute vec3 color;'+
|
|
'varying vec3 vColor;'+
|
|
'void main(void) {' +
|
|
'gl_Position = vec4(coordinates, 1.0);' +
|
|
'vColor = color;'+
|
|
'gl_PointSize = '+ limitdist + '*vSizes;'+
|
|
'}';
|
|
|
|
// Create a vertex shader object
|
|
var vertShader = gl.createShader(gl.VERTEX_SHADER);
|
|
|
|
// Attach vertex shader source code
|
|
gl.shaderSource(vertShader, vertCode);
|
|
|
|
// Compile the vertex shader
|
|
gl.compileShader(vertShader);
|
|
|
|
// fragment shader source code
|
|
var fragCode = `
|
|
#ifdef GL_OES_standard_derivatives
|
|
#extension GL_OES_standard_derivatives : enable
|
|
#endif
|
|
|
|
precision mediump float;
|
|
varying vec3 vColor;
|
|
|
|
void main()
|
|
{
|
|
float r = 0.0, delta = 0.0, alpha = 1.0;
|
|
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
|
|
r = dot(cxy, cxy);
|
|
#ifdef GL_OES_standard_derivatives
|
|
delta = fwidth(r);
|
|
alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r);
|
|
#endif
|
|
|
|
gl_FragColor = vec4(vColor, alpha);
|
|
gl_FragColor.rgb *= gl_FragColor.a;
|
|
}`;
|
|
|
|
gl.getExtension('OES_standard_derivatives');
|
|
|
|
// Create fragment shader object
|
|
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
|
|
|
|
// Attach fragment shader source code
|
|
gl.shaderSource(fragShader, fragCode);
|
|
|
|
// Compile the fragmentt shader
|
|
gl.compileShader(fragShader);
|
|
|
|
// Create a shader program object to
|
|
// store the combined shader program
|
|
var shaderProgram = gl.createProgram();
|
|
|
|
// Attach a vertex shader
|
|
gl.attachShader(shaderProgram, vertShader);
|
|
|
|
// Attach a fragment shader
|
|
gl.attachShader(shaderProgram, fragShader);
|
|
|
|
// Link both the programs
|
|
gl.linkProgram(shaderProgram);
|
|
|
|
// Use the combined shader program object
|
|
gl.useProgram(shaderProgram);
|
|
|
|
|
|
|
|
// Bind vertex buffer object
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
|
|
|
|
// Get the attribute location
|
|
var coord = gl.getAttribLocation(shaderProgram, "coordinates");
|
|
|
|
// point an attribute to the currently bound VBO
|
|
gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
|
|
|
|
// Enable the attribute
|
|
gl.enableVertexAttribArray(coord);
|
|
|
|
// bind the color buffer
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
|
|
|
|
// get the attribute location
|
|
var color = gl.getAttribLocation(shaderProgram, "color");
|
|
|
|
// point attribute to the volor buffer object
|
|
gl.vertexAttribPointer(color, 3, gl.FLOAT, false, 0, 0) ;
|
|
|
|
// enable the color attribute
|
|
gl.enableVertexAttribArray(color);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, size_buffer);
|
|
|
|
var vSize = gl.getAttribLocation(shaderProgram, "vSizes");
|
|
|
|
gl.vertexAttribPointer(vSize, 1, gl.FLOAT, false, 0, 0);
|
|
|
|
gl.enableVertexAttribArray(vSize);
|
|
|
|
// Clear the canvas
|
|
gl.clearColor(1.0, 1.0, 1.0, 1.0);
|
|
|
|
gl.enable( gl.BLEND );
|
|
gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
|
|
|
|
gl.disable(gl.DEPTH_TEST);
|
|
|
|
// Clear the color buffer bit
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
|
|
resize(gl.canvas);
|
|
|
|
gl.viewport(0, 0, dimensions, dimensions);
|
|
|
|
//Draw the triangle
|
|
gl.drawArrays(gl.POINTS, 0, points.length);
|
|
*/
|
|
|