Skip to content

Commit

Permalink
Decomposed 'initialize' in SplomVisualization
Browse files Browse the repository at this point in the history
  • Loading branch information
jtedesco committed Apr 21, 2012
1 parent 6e5b78d commit a25efb2
Showing 1 changed file with 134 additions and 99 deletions.
233 changes: 134 additions & 99 deletions static/scripts/visualizations/SplomVisualization.js
Expand Up @@ -76,6 +76,7 @@ function SplomVisualization(structure, state, predictions) {
};
};


// Build the list form of data entries
var values = [];
for (var name in state) {
Expand All @@ -84,9 +85,11 @@ function SplomVisualization(structure, state, predictions) {
}
}


// Build the list of rack names
var racks = buildRacksData();


// The core data for the visualization
var data = {
traits: traits,
Expand All @@ -95,20 +98,21 @@ function SplomVisualization(structure, state, predictions) {
};


this.initialize = function() {
// Size parameters.
var verticalSize = $('#visualization').height()/data.traits.length - 10,
horizontalSize = $('#visualization').width()/data.traits.length - 10,
padding = 20;

// Size parameters.
var verticalSize = $('#visualization').height()/data.traits.length - 10,
horizontalSize = $('#visualization').width()/data.traits.length - 10,
padding = 20;

// Size parameters.
var n = data.traits.length;
/**
* Convert the node data (in 'data.values') into points for plotting
*/
var convertDataToPoints = function() {

// Position scales.
var x = {}, y = {};
data.traits.forEach(function(trait) {
var value = function(machineData) {
data.traits.forEach(function (trait) {
var value = function (machineData) {
return getCompoundKeyFromDict(machineData, trait);
},
domain = [d3.min(data.values, value), d3.max(data.values, value)],
Expand All @@ -122,6 +126,86 @@ function SplomVisualization(structure, state, predictions) {
.domain(domain)
.range(yRange.slice().reverse());
});
return {x:x, y:y};
};


/**
* Helper function to return the value of some dictionary or nested dictionary by splitting on '.' character
*/
var getCompoundKeyFromDict = function(dictionary, key) {
var keys = key.split('.');
if (keys.length > 2) {
console.log('Cannot use more than one level of nested keys!');
return null;
} else if (keys.length === 2) {
return dictionary[keys[0]][keys[1]];
} else {
return dictionary[keys[0]];
}
};


/**
* Helper function to initialize the brush listeners, for clicking & selecting a rectangle
*/
var initializeBrushes = function(svg, brush, cell, x, y) {
// Attach brush functionality
brush.on("brushstart", onBrushStart)
.on("brush", onBrush)
.on("brushend", onBrushEnd);

// Clear the previously-active brush, if any.
function onBrushStart(p) {
if (brush.data !== p) {
cell.call(brush.clear());
brush.x(x[p.x]).y(y[p.y]).data = p;
}
}

// Highlight the selected circles.
function onBrush(p) {
var e = brush.extent();
svg.selectAll("circle").attr("class", function (d) {

var xVal = getCompoundKeyFromDict(d, p.x);
var yVal = getCompoundKeyFromDict(d, p.y);

return e[0][0] <= xVal && xVal <= e[1][0]
&& e[0][1] <= yVal && yVal <= e[1][1]
? d.rack + ' splomCircle' : 'splomCircle';
});
}

// If the brush is empty, select all circles.
function onBrushEnd() {
if (brush.empty()) svg.selectAll("circle").attr("class", function (d) {
return d.rack + ' splomCircle';
});
}
};


// Helper function for computing the grid of cells
function cross(a, b) {
var c = [], n = a.length, m = b.length, i, j;
for (i = -1; ++i < n;) for (j = -1; ++j < m;) c.push({x:a[i], i:i, y:b[j], j:j});
return c;
}


/**
* Plot this visualization for the first time
*/
this.initialize = function() {

// Size parameters.
var n = data.traits.length;

// Convert the data for nodes into points to be plotted
var __ret = convertDataToPoints();
var x = __ret.x;
var y = __ret.y;

// Axes.
var xAxis = d3.svg.axis()
Expand All @@ -132,10 +216,7 @@ function SplomVisualization(structure, state, predictions) {
.tickSize(verticalSize * n);

// Brush.
var brush = d3.svg.brush()
.on("brushstart", brushstart)
.on("brush", brush)
.on("brushend", brushend);
var brush = d3.svg.brush();

// Root panel.
var svg = d3.select("#visualization").append("svg")
Expand All @@ -147,32 +228,47 @@ function SplomVisualization(structure, state, predictions) {
.data(data.traits)
.enter().append("g")
.attr("class", "x axis")
.attr("transform", function(d, i) { return "translate(" + i * horizontalSize + ",0)"; })
.each(function(d) { d3.select(this).call(yAxis.scale(x[d]).orient("bottom")); });
.attr("transform", function (d, i) {
return "translate(" + i * horizontalSize + ",0)";
})
.each(function (d) {
d3.select(this).call(yAxis.scale(x[d]).orient("bottom"));
});

// Y-axis.
svg.selectAll("g.y.axis")
.data(data.traits)
.enter().append("g")
.attr("class", "y axis")
.attr("transform", function(d, i) { return "translate(0," + i * verticalSize + ")"; })
.each(function(d) { d3.select(this).call(xAxis.scale(y[d]).orient("right")); });
.attr("transform", function (d, i) {
return "translate(0," + i * verticalSize + ")";
})
.each(function (d) {
d3.select(this).call(xAxis.scale(y[d]).orient("right"));
});

// Cell and plot.
var cell = svg.selectAll("g.cell")
.data(cross(data.traits, data.traits))
.enter().append("g")
.attr("class", "cell")
.attr("transform", function(d) { return "translate(" + d.i * horizontalSize + "," + d.j * verticalSize + ")"; })
.attr("transform", function (d) {
return "translate(" + d.i * horizontalSize + "," + d.j * verticalSize + ")";
})
.each(plot);

// Titles for the diagonal.
cell.filter(function(d) { return d.i == d.j; }).append("text")
// Add titles for the diagonal cells
cell.filter(
function (d) {
return d.i == d.j;
}).append("text")
.attr("x", padding)
.attr("y", padding)
.attr("dy", ".71em")
.text(function(d) {
.text(function (d) {
var key = d.x;

// Take only the second part of a compound key, or the full key for a non-compound one
var keys = key.split('.');
if (keys.length >= 2) {
return keys[1];
Expand All @@ -181,6 +277,9 @@ function SplomVisualization(structure, state, predictions) {
}
});

/**
* Plot the visualization for the first time, including all graphs in the matrix and points
*/
function plot(p) {
var cell = d3.select(this);

Expand All @@ -196,14 +295,16 @@ function SplomVisualization(structure, state, predictions) {
cell.selectAll("circle")
.data(data.values)
.enter().append("circle")
.attr("class", function(d) { return d.rack + ' splomCircle'; })
.attr("id", function(node) {
.attr("class", function (d) {
return d.rack + ' splomCircle';
})
.attr("id", function (node) {
return node.name;
})
.attr("cx", function(d) {
.attr("cx", function (d) {
return x[p.x](getCompoundKeyFromDict(d, p.x));
})
.attr("cy", function(d) {
.attr("cy", function (d) {
return y[p.y](getCompoundKeyFromDict(d, p.y));
})
.attr("r", 3);
Expand All @@ -212,89 +313,36 @@ function SplomVisualization(structure, state, predictions) {
cell.call(brush.x(x[p.x]).y(y[p.y]));
}

// Clear the previously-active brush, if any.
function brushstart(p) {
if (brush.data !== p) {
cell.call(brush.clear());
brush.x(x[p.x]).y(y[p.y]).data = p;
}
}

// Highlight the selected circles.
function brush(p) {
var e = brush.extent();
svg.selectAll("circle").attr("class", function(d) {

var xVal = getCompoundKeyFromDict(d, p.x);
var yVal = getCompoundKeyFromDict(d, p.y);

return e[0][0] <= xVal && xVal <= e[1][0]
&& e[0][1] <= yVal && yVal <= e[1][1]
? d.rack + ' splomCircle': 'splomCircle';
});
}

// If the brush is empty, select all circles.
function brushend() {
if (brush.empty()) svg.selectAll("circle").attr("class", function(d) {
return d.rack + ' splomCircle';
});
}

function cross(a, b) {
var c = [], n = a.length, m = b.length, i, j;
for (i = -1; ++i < n;) for (j = -1; ++j < m;) c.push({x: a[i], i: i, y: b[j], j: j});
return c;
}

// Setup the brush listeners (for clicking & selecting points)
initializeBrushes(svg, brush, cell, x, y);

// Reveal the visualization
showVisualization();
};

this.update = function() {

// Size parameters.
var verticalSize = $('#visualization').height()/data.traits.length - 10,
horizontalSize = $('#visualization').width()/data.traits.length - 10,
padding = 20;

// Position scales.
var x = {}, y = {};
data.traits.forEach(function(trait) {
var value = function(machineData) {
return getCompoundKeyFromDict(machineData, trait);
},
domain = [d3.min(data.values, value), d3.max(data.values, value)],
xRange = [padding / 2, horizontalSize - padding / 2],
yRange = [padding / 2, verticalSize - padding / 2];
x[trait] = d3.scale.linear()
.domain(domain)
.range(xRange);

y[trait] = d3.scale.linear()
.domain(domain)
.range(yRange.slice().reverse());
});
// Convert the data for nodes into points to be plotted
var __ret = convertDataToPoints();
var x = __ret.x;
var y = __ret.y;

function replot(p) {

var cell = d3.select(this);

// Plot dots.
// Plot dots, animating from their previous positions to new ones, and old radii to new ones
cell.selectAll("circle")
.attr("cx", function(d) {
return x[p.x](getCompoundKeyFromDict(d, p.x));
})
.transition()
.duration(300)
.attr("cy", function(d) {
return y[p.y](getCompoundKeyFromDict(d, p.y));
})
.transition()
.duration(300)
.attr("r", 3)
.transition()
.duration(300);
}


Expand All @@ -308,17 +356,4 @@ function SplomVisualization(structure, state, predictions) {
this.title = function() {
return "Scatterplot Matrix Showing Probabilities of Event Types by Rack"
};

// Helper function to return the value of some dictionary or nested dictionary by splitting on '.' character
var getCompoundKeyFromDict = function(dictionary, key) {
var keys = key.split('.');
if (keys.length > 2) {
console.log('Cannot use more than one level of nested keys!');
return null;
} else if (keys.length === 2) {
return dictionary[keys[0]][keys[1]];
} else {
return dictionary[keys[0]];
}
};
}

0 comments on commit a25efb2

Please sign in to comment.