lineWithFocus now rescales the Y (needs optimization). Added different testing data. Added cumulative line example (still needs work, but functions).

master-patched
Bob Monteverde 12 years ago
parent d1ae59f84e
commit abfa54d48b

@ -0,0 +1,169 @@
<!DOCTYPE html>
<meta charset="utf-8">
<link href="../src/d3.css" rel="stylesheet" type="text/css">
<style>
body {
overflow-y:scroll;
}
text {
font: 12px sans-serif;
}
</style>
<body>
<div id="test1">
<svg></svg>
</div>
<script src="../lib/d3.v2.js"></script>
<script src="../lib/jquery.min.js"></script>
<script src="../nv.d3.js"></script>
<script src="../src/nvtooltip.js"></script>
<script src="../src/models/legend.js"></script>
<script src="../src/models/xaxis.js"></script>
<script src="../src/models/yaxis.js"></script>
<script src="../src/models/line.js"></script>
<script src="../src/models/cumulativeLine.js"></script>
<script>
var cumulativeTestData = [
{
key: "Long",
values: [ [ 1201755600000 , -6.774298931796] , [ 1204261200000 , -11.422461282769] , [ 1206936000000 , -10.219916615529] , [ 1209528000000 , -0.56407600751219] , [ 1212206400000 , 4.6840307972664] , [ 1214798400000 , -3.9248672389375] , [ 1217476800000 , -6.0716225772829] , [ 1220155200000 , -5.7778068668664] , [ 1222747200000 , -23.305053130956] , [ 1225425600000 , -36.265703536880] , [ 1228021200000 , -42.370839876110] , [ 1230699600000 , -40.980153086928] , [ 1233378000000 , -43.453115415726] , [ 1235797200000 , -47.126268843788] , [ 1238472000000 , -45.640450538503] , [ 1241064000000 , -39.069177821319] , [ 1243742400000 , -32.473149326513] , [ 1246334400000 , -32.839058643003] , [ 1249012800000 , -24.086484363256] , [ 1251691200000 , -20.778110994079] , [ 1254283200000 , -14.680659434995] , [ 1256961600000 , -18.037208993777] , [ 1259557200000 , -11.029707170002] , [ 1262235600000 , -10.290150526116] , [ 1264914000000 , -15.275247251944] , [ 1267333200000 , -13.193884134335] , [ 1270008000000 , -5.2907717177462] , [ 1272600000000 , -3.9721079979652] , [ 1275278400000 , -14.621491396228] , [ 1277870400000 , -17.834038951494] , [ 1280548800000 , -9.7980978780076] , [ 1283227200000 , -12.367670799178] , [ 1285819200000 , 5.0872546622468] , [ 1288497600000 , 9.8628117206068] , [ 1291093200000 , 9.6821647981428] , [ 1293771600000 , 15.542754406224] , [ 1296450000000 , 15.169718066427] , [ 1298869200000 , 20.633132200089] , [ 1301544000000 , 20.947418513749] , [ 1304136000000 , 27.567424162979] , [ 1306814400000 , 24.901938361681] , [ 1309406400000 , 22.497073375650] , [ 1312084800000 , 21.987088423124] , [ 1314763200000 , 3.1254197967874] , [ 1317355200000 , -8.0186535759880] , [ 1320033600000 , 1.0475042879104] , [ 1322629200000 , -2.1426714011605] , [ 1325307600000 , -5.2893935115000] , [ 1327986000000 , 6.1945068788305] , [ 1330491600000 , 13.100019228710] , [ 1333166400000 , 15.238895035959]]
},
{
key: "Short",
values: [ [ 1201755600000 , 1.6967185806565] , [ 1204261200000 , 2.9544605100342] , [ 1206936000000 , 3.5676649424863] , [ 1209528000000 , 0.70848661177785] , [ 1212206400000 , -1.7076190259865] , [ 1214798400000 , 7.8105595020039] , [ 1217476800000 , 8.5476021506446] , [ 1220155200000 , 7.1576538909335] , [ 1222747200000 , 14.086479472107] , [ 1225425600000 , 26.638902767663] , [ 1228021200000 , 29.002916299172] , [ 1230699600000 , 23.336734970188] , [ 1233378000000 , 25.785466305977] , [ 1235797200000 , 30.826840805638] , [ 1238472000000 , 28.381484497177] , [ 1241064000000 , 21.759783928799] , [ 1243742400000 , 18.166465772204] , [ 1246334400000 , 18.161281272431] , [ 1249012800000 , 13.730944119173] , [ 1251691200000 , 12.667881940682] , [ 1254283200000 , 10.038695304926] , [ 1256961600000 , 10.957569008401] , [ 1259557200000 , 9.8350884213349] , [ 1262235600000 , 8.3833563781759] , [ 1264914000000 , 10.456986736208] , [ 1267333200000 , 9.5627923118989] , [ 1270008000000 , 4.5053427131636] , [ 1272600000000 , 3.5720754748957] , [ 1275278400000 , 10.347684404293] , [ 1277870400000 , 10.222585554753] , [ 1280548800000 , 6.8089787796259] , [ 1283227200000 , 12.714884589143] , [ 1285819200000 , 4.1572619086765] , [ 1288497600000 , 0.26499294242447] , [ 1291093200000 , 0.86369284151557] , [ 1293771600000 , -4.9641630596314] , [ 1296450000000 , -6.7956990273974] , [ 1298869200000 , -9.6706033017749] , [ 1301544000000 , -10.697126031426] , [ 1304136000000 , -13.637090599569] , [ 1306814400000 , -11.977608760351] , [ 1309406400000 , -8.8934853811678] , [ 1312084800000 , -5.7780351819777] , [ 1314763200000 , 0.013928580370608] , [ 1317355200000 , 7.8721613921440] , [ 1320033600000 , 4.5239162085138] , [ 1322629200000 , 7.1293313800248] , [ 1325307600000 , 8.9699302202310] , [ 1327986000000 , 3.4331637128227] , [ 1330491600000 , 0.87231281087281] , [ 1333166400000 , 0.34597419296371]]
},
{
key: "Gross",
values: [ [ 1201755600000 , -5.0775803511395] , [ 1204261200000 , -8.4680007727348] , [ 1206936000000 , -6.6522516730427] , [ 1209528000000 , 0.14441060426566] , [ 1212206400000 , 2.9764117712799] , [ 1214798400000 , 3.8856922630664] , [ 1217476800000 , 2.4759795733617] , [ 1220155200000 , 1.3798470240671] , [ 1222747200000 , -9.218573658849] , [ 1225425600000 , -9.626800769217] , [ 1228021200000 , -13.367923576938] , [ 1230699600000 , -17.643418116740] , [ 1233378000000 , -17.667649109749] , [ 1235797200000 , -16.299428038150] , [ 1238472000000 , -17.258966041326] , [ 1241064000000 , -17.309393892520] , [ 1243742400000 , -14.306683554309] , [ 1246334400000 , -14.677777370572] , [ 1249012800000 , -10.355540244083] , [ 1251691200000 , -8.110229053397] , [ 1254283200000 , -4.641964130069] , [ 1256961600000 , -7.079639985376] , [ 1259557200000 , -1.1946187486671] , [ 1262235600000 , -1.9067941479401] , [ 1264914000000 , -4.818260515736] , [ 1267333200000 , -3.6310918224361] , [ 1270008000000 , -0.7854290045826] , [ 1272600000000 , -0.4000325230695] , [ 1275278400000 , -4.273806991935] , [ 1277870400000 , -7.611453396741] , [ 1280548800000 , -2.9891190983817] , [ 1283227200000 , 0.347213789965] , [ 1285819200000 , 9.2445165709233] , [ 1288497600000 , 10.127804663031] , [ 1291093200000 , 10.545857639658] , [ 1293771600000 , 10.578591346593] , [ 1296450000000 , 8.3740190390296] , [ 1298869200000 , 10.962528898314] , [ 1301544000000 , 10.250292482323] , [ 1304136000000 , 13.930333563410] , [ 1306814400000 , 12.924329601330] , [ 1309406400000 , 13.603587994482] , [ 1312084800000 , 16.209053241146] , [ 1314763200000 , 3.1393483771580] , [ 1317355200000 , -0.1464921838440] , [ 1320033600000 , 5.5714204964242] , [ 1322629200000 , 4.9866599788643] , [ 1325307600000 , 3.6805367087310] , [ 1327986000000 , 9.6276705916532] , [ 1330491600000 , 13.972332039583] , [ 1333166400000 , 15.584869228923]]
},
{
key: "S&P 1500",
values: [ [ 1201755600000 , -5.9710800189518] , [ 1204261200000 , -8.9186908887917] , [ 1206936000000 , -9.3265754909255] , [ 1209528000000 , -4.7381178885542] , [ 1212206400000 , -3.0912920812728] , [ 1214798400000 , -11.116204586183] , [ 1217476800000 , -11.845729061204] , [ 1220155200000 , -10.462970905824] , [ 1222747200000 , -18.501870830803] , [ 1225425600000 , -32.616659607913] , [ 1228021200000 , -37.668891164316] , [ 1230699600000 , -36.722829020553] , [ 1233378000000 , -42.100391912416] , [ 1235797200000 , -48.249838295680] , [ 1238472000000 , -43.714511725956] , [ 1241064000000 , -37.944007905028] , [ 1243742400000 , -34.706605423145] , [ 1246334400000 , -34.526285928477] , [ 1249012800000 , -29.449295179018] , [ 1251691200000 , -26.893738314183] , [ 1254283200000 , -24.013011151663] , [ 1256961600000 , -25.698073456930] , [ 1259557200000 , -21.445628470490] , [ 1262235600000 , -19.480335623731] , [ 1264914000000 , -22.346030236959] , [ 1267333200000 , -19.775463207788] , [ 1270008000000 , -14.812442842643] , [ 1272600000000 , -13.148750272697] , [ 1275278400000 , -20.001724517317] , [ 1277870400000 , -24.333509222471] , [ 1280548800000 , -19.056593021706] , [ 1283227200000 , -22.826146186356] , [ 1285819200000 , -15.718227905522] , [ 1288497600000 , -12.522888999313] , [ 1291093200000 , -12.181363414422] , [ 1293771600000 , -6.2892434050551] , [ 1296450000000 , -4.1751220461218] , [ 1298869200000 , -0.75769110337973] , [ 1301544000000 , -0.40521206856089] , [ 1304136000000 , 2.5093887670054] , [ 1306814400000 , 1.3387467892848] , [ 1309406400000 , -0.38763177561303] , [ 1312084800000 , -2.5850739507076] , [ 1314763200000 , -8.0958326971042] , [ 1317355200000 , -14.939943551408] , [ 1320033600000 , -5.3273405660650] , [ 1322629200000 , -5.5140388315835] , [ 1325307600000 , -4.6491354185989] , [ 1327986000000 , -0.13977544505504] , [ 1330491600000 , 4.1131148964748] , [ 1333166400000 , 6.9778910857160]]
}
].map(function(line) {
line.values = line.values.map(function(d) { return {x: d[0], y: d[1]/100 }});
return line;
});
nv.addGraph({
generate: function() {
var width = $(window).width() - 40,
height = $(window).height() - 40;
var chart = nv.models.cumulativeLine()
.width(width)
.height(height)
//.margin({top: 20, right: 10, bottom: 50, left: 80})
//chart.yAxis.axisLabel('Cumulative');
//chart.xAxis.axisLabel('Date');
chart.yAxis.tickFormat(d3.format(',%'))
chart.xAxis.tickFormat(function(d) {
return d3.time.format('%x')(new Date(d))
})
//chart.xaxis.tickFormat(d3.format(".02f"))
var svg = d3.select('#test1 svg')
.attr('width', width)
.attr('height', height)
.datum(cumulativeTestData)
svg.transition().duration(500).call(chart);
return chart;
},
callback: function(graph) {
graph.dispatch.on('tooltipShow', function(e) {
var offset = $('#test1').offset(),
left = e.pos[0] + offset.left,
top = e.pos[1] + offset.top,
formatterY = d3.format(",.2%"),
formatterX = function(d) {
return d3.time.format('%x')(new Date(d))
};
var content = '<h3>' + e.series.key + '</h3>' +
'<p>' +
formatterY(graph.y()(e.point)) + ' on ' + formatterX(graph.x()(e.point)) +
'</p>';
//$('#positionTest').css({'left': left, 'top': top});
nvtooltip.show([left, top], content);
});
graph.dispatch.on('tooltipHide', function(e) {
nvtooltip.cleanup();
});
$(window).resize(function() {
var width = $(window).width() - 40,
height = $(window).height() - 40,
margin = graph.margin();
if (width < margin.left + margin.right + 20)
width = margin.left + margin.right + 20;
if (height < margin.top + margin.bottom + 20)
height = margin.top + margin.bottom + 20;
graph
.width(width)
.height(height);
d3.select('#test1 svg')
.attr('width', width)
.attr('height', height)
.call(graph);
});
}
});
function sinAndCos() {
var sin = [],
cos = [];
for (var i = 0; i < 100; i++) {
sin.push({x: i, y: Math.sin(i/10)});
cos.push({x: i, y: .5 * Math.cos(i/10)});
}
return [
{
values: sin,
key: "Sine Wave",
color: "#ff7f0e"
},
{
values: cos,
key: "Cosine Wave",
color: "#2ca02c"
}
];
}
</script>

@ -30,8 +30,15 @@ text {
<script src="../src/models/yaxis.js"></script>
<script src="../src/models/line.js"></script>
<script src="../src/models/lineWithFocus.js"></script>
<script src="stream_layers.js"></script>
<script>
var test_data = stream_layers(3,128,.1).map(function(data, i) {
return {
key: 'Stream' + i,
values: data
};
});
nv.addGraph({
generate: function() {
@ -41,6 +48,7 @@ nv.addGraph({
var chart = nv.models.lineWithFocus()
.width(width)
.height(height)
//.dotRadius(1) //TODO: fix this setting
.yTickFormat(d3.format('.2r'))
.xTickFormat(d3.format('.2r'))
@ -48,7 +56,7 @@ nv.addGraph({
var svg = d3.select('#test1 svg')
.attr('width', width)
.attr('height', height)
.datum(sinAndCos());
.datum(test_data);
svg.transition().duration(500).call(chart);

@ -0,0 +1,35 @@
/* Inspired by Lee Byron's test data generator. */
function stream_layers(n, m, o) {
if (arguments.length < 3) o = 0;
function bump(a) {
var x = 1 / (.1 + Math.random()),
y = 2 * Math.random() - .5,
z = 10 / (.1 + Math.random());
for (var i = 0; i < m; i++) {
var w = (i / m - y) * z;
a[i] += x * Math.exp(-w * w);
}
}
return d3.range(n).map(function() {
var a = [], i;
for (i = 0; i < m; i++) a[i] = o + o * Math.random();
for (i = 0; i < 5; i++) bump(a);
return a.map(stream_index);
});
}
/* Another layer generator using gamma distributions. */
function stream_waves(n, m) {
return d3.range(n).map(function(i) {
return d3.range(m).map(function(j) {
var x = 20 * j / m - i / 3;
return 2 * x * Math.exp(-.5 * x);
}).map(stream_index);
});
}
function stream_index(d, i) {
return {x: i, y: Math.max(0, d)};
}

@ -62,6 +62,16 @@
*/
svg {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
text {
font: 12px sans-serif;
}
@ -188,6 +198,13 @@ text {
}
.indexLine {
cursor: ew-resize;
}
/**********
* Scatter
*/

@ -0,0 +1,269 @@
nv.models.cumulativeLine = function() {
var margin = {top: 30, right: 20, bottom: 50, left: 60},
width = 960,
height = 500,
dotRadius = function() { return 2.5 },
color = d3.scale.category10().range(),
dispatch = d3.dispatch('tooltipShow', 'tooltipHide'),
index = {x:0, i:0};
var x = d3.scale.linear(),
dx = d3.scale.linear(),
y = d3.scale.linear(),
getX = function(d) { return d.x },
getY = function(d) { return d.y },
xAxis = nv.models.xaxis().scale(x),
yAxis = nv.models.yaxis().scale(y),
legend = nv.models.legend().height(30),
lines = nv.models.line();
var updateChart;
var indexDrag = d3.behavior.drag()
.on('dragstart', dragStart)
.on('drag', dragMove)
.on('dragend', dragEnd);
function dragStart(d,i) {}
function dragMove(d,i) {
d.x += d3.event.dx;
//if (d.x > x.range()[0]) d.x = x.range()[0];
if (d.x < 0) d.x = 0;
if (d.x > x.range()[1]) d.x = x.range()[1];
index.i = Math.round(dx.invert(d.x));
d3.select(this).attr("transform", "translate(" + dx(d.i) + ",0)");
}
function dragEnd(d,i) {
updateChart(); //could do thisinside of dragMove, but would need to disable transitions.. and performance may be questionable
}
function chart(selection) {
//TODO: Think of a better way to update the chart from moving the 'indexLine'
updateChart = function() {
selection.call(chart);
};
selection.each(function(data) {
data = indexify(index.i, data);
var series = data.filter(function(d) { return !d.disabled })
.map(function(d) { return d.values });
x .domain(d3.extent(d3.merge(series), getX ))
.range([0, width - margin.left - margin.right]);
dx .domain([0, data[0].values.length - 1])
.range([0, width - margin.left - margin.right]);
y .domain(d3.extent(d3.merge(series), getY ))
.range([height - margin.top - margin.bottom, 0]);
lines
.width(width - margin.left - margin.right)
.height(height - margin.top - margin.bottom)
.color(data.map(function(d,i) {
return d.color || color[i % 10];
}).filter(function(d,i) { return !data[i].disabled }))
var wrap = d3.select(this).selectAll('g.wrap').data([data]);
var gEnter = wrap.enter().append('g').attr('class', 'wrap d3lineWithLegend').append('g');
gEnter.append('g').attr('class', 'x axis');
gEnter.append('g').attr('class', 'y axis');
gEnter.append('g').attr('class', 'linesWrap');
gEnter.append('g').attr('class', 'legendWrap');
legend.dispatch.on('legendClick', function(d,i) {
d.disabled = !d.disabled;
if (!data.filter(function(d) { return !d.disabled }).length) {
data.map(function(d) {
d.disabled = false;
wrap.selectAll('.series').classed('disabled', false);
return d;
});
}
selection.transition().call(chart);
});
legend.dispatch.on('legendMouseover', function(d, i) {
d.hover = true;
selection.transition().call(chart)
});
legend.dispatch.on('legendMouseout', function(d, i) {
d.hover = false;
selection.transition().call(chart)
});
lines.dispatch.on('pointMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
pos: [e.pos[0] + margin.left, e.pos[1] + margin.top],
seriesIndex: e.seriesIndex,
pointIndex: e.pointIndex
});
});
lines.dispatch.on('pointMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});
//TODO: margins should be adjusted based on what components are used: axes, axis labels, legend
margin.top = legend.height();
var g = wrap.select('g')
.attr('transform', 'translate(' + margin.left + ',' + legend.height() + ')');
legend.width(width/2 - margin.right);
g.select('.legendWrap')
.datum(data)
.attr('transform', 'translate(' + (width/2 - margin.left) + ',' + (-legend.height()) +')')
.call(legend);
var linesWrap = g.select('.linesWrap')
.datum(data.filter(function(d) { return !d.disabled }))
d3.transition(linesWrap).call(lines);
//g.select('.linesWrap').selectAll('.indexLine')
var indexLine = linesWrap.selectAll('.indexLine')
.data([index]);
indexLine.enter().append('rect').attr('class', 'indexLine')
.attr('width', 3)
.attr('height', height - margin.top - margin.bottom)
.attr('x', -2)
.attr('fill', 'red')
.attr('fill-opacity', .5)
.call(indexDrag)
indexLine
.attr("transform", function(d) { return "translate(" + dx(d.i) + ",0)" })
xAxis
.domain(x.domain())
.range(x.range())
.ticks( width / 100 )
.tickSize(-(height - margin.top - margin.bottom), 0);
g.select('.x.axis')
.attr('transform', 'translate(0,' + y.range()[0] + ')');
d3.transition(g.select('.x.axis'))
.call(xAxis);
yAxis
.domain(y.domain())
.range(y.range())
.ticks( height / 36 )
.tickSize(-(width - margin.right - margin.left), 0);
d3.transition(g.select('.y.axis'))
.call(yAxis);
});
return chart;
}
/* Normalize the data according to an index point. */
function indexify(idx, data) {
return data.map(function(line, i) {
var v = getY(line.values[idx]);
return {
key: line.key,
values: line.values.map(function(point) {
/*
if (line.key == "Short") {
if ((options.yaxis.type == 'percent' && v < -90) || (options.yaxis.type == 'bps' && v < -90000)) {
shortDisabled = i;
return {'x': point.x, 'y': 0 };
} else {
shortDisabled = -1;
}
}
*/
return {'x': getX(point), 'y': (getY(point) - v) / (1 + v) };
})
};
});
};
chart.dispatch = dispatch;
chart.x = function(_) {
if (!arguments.length) return getX;
getX = _;
lines.x(_);
return chart;
};
chart.y = function(_) {
if (!arguments.length) return getY;
getY = _;
lines.y(_);
return chart;
};
chart.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.dotRadius = function(_) {
if (!arguments.length) return dotRadius;
dotRadius = d3.functor(_);
lines.dotRadius = _;
return chart;
};
// Expose the x-axis' tickFormat method.
//chart.xAxis = {};
//d3.rebind(chart.xAxis, xAxis, 'tickFormat');
chart.xAxis = xAxis;
// Expose the y-axis' tickFormat method.
//chart.yAxis = {};
//d3.rebind(chart.yAxis, yAxis, 'tickFormat');
chart.yAxis = yAxis;
return chart;
}

@ -67,7 +67,8 @@ nv.models.line = function() {
var vertices = d3.merge(data.map(function(line, lineIndex) {
return line.values.map(function(point, pointIndex) {
return [x(getX(point)), y(getY(point)), lineIndex, pointIndex]; //inject series and point index for reference into voronoi
//return [x(getX(point)), y(getY(point)), lineIndex, pointIndex]; //inject series and point index for reference into voronoi
return [x(getX(point)) * (Math.random() / 1e12 + 1) , y(getY(point)) * (Math.random() / 1e12 + 1), lineIndex, pointIndex]; //temp hack to add noise untill I think of a better way so there are no duplicates
})
})
);

@ -30,12 +30,13 @@ nv.models.lineWithFocus = function() {
.on('brush', onBrush);
var wrap, gEnter, g, focus, focusLines, contextWrap, focusWrap, contextLines; //brought all variables to this scope for use within function... is this a bad idea?
var wrap, gEnter, g, focus, focusLines, contextWrap, focusWrap, contextLines; //brought all variables to this scope for use within brush function... is this a bad idea?
var seriesData; //Temporarily bringing this data to this scope.... may be bad idea (same with above).. may need to rethink brushing
function chart(selection) {
selection.each(function(data) {
var seriesData = data.filter(function(d) { return !d.disabled })
seriesData = data.filter(function(d) { return !d.disabled })
.map(function(d) { return d.values });
x2 .domain(d3.extent(d3.merge(seriesData), getX ))
@ -234,13 +235,26 @@ nv.models.lineWithFocus = function() {
// ********** FUNCTIONS **********
function onBrush() {
var yDomain = brush.empty() ? y2.domain() : d3.extent(d3.merge(seriesData).filter(function(d) {
return getX(d) >= brush.extent()[0] && getX(d) <= brush.extent()[1];
}), getY);
if (typeof yDomain[0] == 'undefined') yDomain = y2.domain();
x.domain(brush.empty() ? x2.domain() : brush.extent());
y.domain(yDomain);
//y.domain(brush.empty() ? y2.domain() : d3.extent(d3.merge(seriesData).filter(function(d) {
//return getX(d) >= brush.extent()[0] && getX(d) <= brush.extent()[1];
//}), getY) || y2.domain() );
focus.xDomain(x.domain());
focus.yDomain(y.domain());
focusLines.call(focus)
wrap.select('.x.axis').call(xAxis);
wrap.select('.y.axis').call(yAxis);
}

Loading…
Cancel
Save