renamed dispatch point**** to element*** to be consistent with the others, cleaned up scatter, line, and stacked area to utilize scatter's scales, fixed a couple of remaps

master-patched
Bob Monteverde 12 years ago
parent 7857c4ab6b
commit 030b99070c

@ -41,7 +41,7 @@ text {
<script src="stream_layers.js"></script>
<script>
var test_data = stream_layers(3,128,.1).map(function(data, i) {
var test_data = stream_layers(3,100,.1).map(function(data, i) {
return {
key: 'Stream' + i,
values: data

@ -128,6 +128,7 @@ var width = function() { return parseInt(d3.select('#chart').style('width')) },
var chart = nv.models.stackedAreaWithLegend()
.width(width)
.height(height)
.clipEdge(true)
//.y(function(d) { return d.ytest }) //not ready yet for custom accessor, almost there
chart.xAxis

@ -251,29 +251,28 @@ nv.utils.windowSize = function() {
nv.models.axis = function() {
var domain = [0,1], //just to have something to start with, maybe I dont need this
range = [0,1],
orient = 'bottom',
axisLabelText = false;
//Default Settings
var scale = d3.scale.linear(),
axis = d3.svg.axis().scale(scale);
axisLabelText = null,
highlightZero = true;
//TODO: considering adding margin
var axis = d3.svg.axis()
.scale(scale)
.orient('bottom');
function chart(selection) {
selection.each(function(data) {
//scale.domain(domain)
//.range(range);
//axis.orient(orient);
if (orient == 'top' || orient == 'bottom')
if (axis.orient() == 'top' || axis.orient() == 'bottom')
axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);
//TODO: consider calculating height based on whether or not label is added, for reference in charts using this component
var axisLabel = d3.select(this).selectAll('text.axislabel')
.data([axisLabelText || null]);
switch (orient) {
axisLabel.exit().remove();
switch (axis.orient()) {
case 'top':
axisLabel.enter().append('text').attr('class', 'axislabel')
.attr('text-anchor', 'middle')
@ -303,19 +302,17 @@ nv.models.axis = function() {
.attr('x', -scale.range()[0] / 2);
break;
}
axisLabel.exit().remove();
axisLabel
.text(function(d) { return d });
//d3.select(this)
d3.transition(d3.select(this))
.call(axis);
if (orient == 'left' || orient == 'right')
//highlight zero line
if (highlightZero)
d3.select(this)
.selectAll('line.tick')
//.filter(function(d) { return !parseFloat(d) })
.filter(function(d) { return !parseFloat(Math.round(d*100000)/1000000) }) //this is because sometimes the 0 tick is a very small fraction, TODO: think of cleaner technique
.classed('zero', true);
@ -325,43 +322,29 @@ nv.models.axis = function() {
}
//TODO: orient, domain, and range could be rebind's... but then they won't return the chart component
chart.orient = function(_) {
if (!arguments.length) return orient;
orient = _;
axis.orient(_);
return chart;
};
d3.rebind(chart, axis, 'orient', 'ticks', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); //these are also accessible by chart.scale(), but added common ones directly for ease of use
chart.domain = function(_) {
if (!arguments.length) return domain;
//domain = _;
scale.domain(_);
chart.axisLabel = function(_) {
if (!arguments.length) return axisLabelText;
axisLabelText = _;
return chart;
};
}
chart.range = function(_) {
if (!arguments.length) return range;
//range = _;
scale.range(_);
chart.highlightZero = function(_) {
if (!arguments.length) return highlightZero;
highlightZero = _;
return chart;
};
}
chart.scale = function(_) {
if (!arguments.length) return scale;
scale = _;
axis.scale(scale);
return chart;
};
chart.axisLabel = function(_) {
if (!arguments.length) return axisLabelText;
axisLabelText = _;
d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands');
return chart;
}
//d3.rebind(chart, scale, 'domain', 'range', 'rangRoundBands', 'rangeBands'); //would implement, but will break because domain and range won't return chart... will likelu implement later
d3.rebind(chart, axis, 'ticks', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
return chart;
}
@ -1331,7 +1314,7 @@ nv.models.cumulativeLine = function() {
});
*/
lines.dispatch.on('pointMouseover.tooltip', function(e) {
lines.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -1341,7 +1324,7 @@ nv.models.cumulativeLine = function() {
});
});
lines.dispatch.on('pointMouseout.tooltip', function(e) {
lines.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});
@ -1885,7 +1868,7 @@ nv.models.linePlusBar = function() {
});
lines.dispatch.on('pointMouseover.tooltip', function(e) {
lines.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -1895,7 +1878,7 @@ nv.models.linePlusBar = function() {
});
});
lines.dispatch.on('pointMouseout.tooltip', function(e) {
lines.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});
@ -2253,7 +2236,7 @@ nv.models.lineWithFocus = function() {
});
*/
focus.dispatch.on('pointMouseover.tooltip', function(e) {
focus.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -2262,7 +2245,7 @@ nv.models.lineWithFocus = function() {
pointIndex: e.pointIndex
});
});
focus.dispatch.on('pointMouseout.tooltip', function(e) {
focus.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});
@ -2519,7 +2502,7 @@ nv.models.lineWithLegend = function() {
});
*/
lines.dispatch.on('pointMouseover.tooltip', function(e) {
lines.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -2529,7 +2512,7 @@ nv.models.lineWithLegend = function() {
});
});
lines.dispatch.on('pointMouseout.tooltip', function(e) {
lines.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});
@ -3380,16 +3363,15 @@ nv.models.scatter = function() {
var x = d3.scale.linear(),
y = d3.scale.linear(),
z = d3.scale.sqrt(), //sqrt because point size is done by area, not radius
dispatch = d3.dispatch('pointClick', 'pointMouseover', 'pointMouseout'),//TODO: consider renaming to elementMouseove and elementMouseout for consistency
dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout'),//TODO: consider renaming to elementMouseove and elementMouseout for consistency
x0, y0, z0,
timeoutID;
function chart(selection) {
selection.each(function(data) {
//var seriesData = data.map(function(d) {
//var seriesData = data.map(function(d) {
var seriesData = (xDomain && yDomain && sizeDomain) ? [] : // if we know xDomain and yDomain and sizeDomain, no need to calculate.... if Size is constant remember to set sizeDomain to speed up performance
//console.log('recalculating');
data.map(function(d) {
data.map(function(d) {
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i), size: getSize(d,i) }
})
@ -3405,8 +3387,6 @@ nv.models.scatter = function() {
//add series index to each data point for reference
data = data.map(function(series, i) {
series.values = series.values.map(function(point) {
//point.label = series.label;
//point.color = series.color;
point.series = i;
return point;
});
@ -3415,6 +3395,7 @@ nv.models.scatter = function() {
//TODO: figure out the best way to deal with scales with equal MIN and MAX
//TODO: think of a good way to re-use scales
x .domain(xDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.x }).concat(forceX)))
.range([0, availableWidth]);
@ -3505,7 +3486,7 @@ nv.models.scatter = function() {
var series = data[d.series],
point = series.values[d.point];
dispatch.pointClick({
dispatch.elementClick({
point: point,
series:series,
pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
@ -3517,7 +3498,7 @@ nv.models.scatter = function() {
var series = data[d.series],
point = series.values[d.point];
dispatch.pointMouseover({
dispatch.elementMouseover({
point: point,
series:series,
pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
@ -3526,7 +3507,7 @@ nv.models.scatter = function() {
});
})
.on('mouseout', function(d, i) {
dispatch.pointMouseout({
dispatch.elementMouseout({
point: data[d.series].values[d.point],
series: data[d.series],
seriesIndex: d.series,
@ -3534,12 +3515,12 @@ nv.models.scatter = function() {
});
});
dispatch.on('pointMouseover.point', function(d) {
dispatch.on('elementMouseover.point', function(d) {
wrap.select('.series-' + d.seriesIndex + ' .point-' + d.pointIndex)
.classed('hover', true);
});
dispatch.on('pointMouseout.point', function(d) {
dispatch.on('elementMouseout.point', function(d) {
wrap.select('.series-' + d.seriesIndex + ' circle.point-' + d.pointIndex)
.classed('hover', false);
});
@ -3907,7 +3888,7 @@ nv.models.scatterWithLegend = function() {
*/
scatter.dispatch.on('pointMouseover.tooltip', function(e) {
scatter.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -3917,18 +3898,18 @@ nv.models.scatterWithLegend = function() {
});
});
scatter.dispatch.on('pointMouseout.tooltip', function(e) {
scatter.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});
scatter.dispatch.on('pointMouseover.dist', function(d) {
scatter.dispatch.on('elementMouseover.dist', function(d) {
scatterWrap.select('.series-' + d.seriesIndex + ' .distX-' + d.pointIndex)
.attr('y1', d.pos[1]);
scatterWrap.select('.series-' + d.seriesIndex + ' .distY-' + d.pointIndex)
.attr('x1', d.pos[0]);
});
scatter.dispatch.on('pointMouseout.dist', function(d) {
scatter.dispatch.on('elementMouseout.dist', function(d) {
scatterWrap.select('.series-' + d.seriesIndex + ' .distX-' + d.pointIndex)
.attr('y1', y.range()[0]);
scatterWrap.select('.series-' + d.seriesIndex + ' .distY-' + d.pointIndex)
@ -4442,13 +4423,13 @@ nv.models.stackedArea = function() {
//TODO: these disptach handlers don't need to be called everytime the chart
// is called, but 'g' is only in this scope... so need to rethink.
scatter.dispatch.on('pointClick.area', function(e) {
scatter.dispatch.on('elementClick.area', function(e) {
dispatch.areaClick(e);
})
scatter.dispatch.on('pointMouseover.area', function(e) {
scatter.dispatch.on('elementMouseover.area', function(e) {
g.select('.area-' + e.seriesIndex).classed('hover', true);
});
scatter.dispatch.on('pointMouseout.area', function(e) {
scatter.dispatch.on('elementMouseout.area', function(e) {
g.select('.area-' + e.seriesIndex).classed('hover', false);
});
@ -4545,12 +4526,12 @@ nv.models.stackedArea = function() {
chart.scatter = scatter;
scatter.dispatch.on('pointMouseover.tooltip', function(e) {
scatter.dispatch.on('elementMouseover.tooltip', function(e) {
e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
dispatch.tooltipShow(e);
});
scatter.dispatch.on('pointMouseout.tooltip', function(e) {
scatter.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});

6
nv.d3.min.js vendored

File diff suppressed because one or more lines are too long

@ -1,28 +1,27 @@
nv.models.axis = function() {
var domain = [0,1], //just to have something to start with, maybe I dont need this
range = [0,1],
orient = 'bottom',
axisLabelText = false;
//Default Settings
var scale = d3.scale.linear(),
axis = d3.svg.axis().scale(scale);
axisLabelText = null,
highlightZero = true;
//TODO: considering adding margin
var axis = d3.svg.axis()
.scale(scale)
.orient('bottom');
function chart(selection) {
selection.each(function(data) {
//scale.domain(domain)
//.range(range);
//axis.orient(orient);
if (orient == 'top' || orient == 'bottom')
if (axis.orient() == 'top' || axis.orient() == 'bottom')
axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);
//TODO: consider calculating height based on whether or not label is added, for reference in charts using this component
var axisLabel = d3.select(this).selectAll('text.axislabel')
.data([axisLabelText || null]);
switch (orient) {
axisLabel.exit().remove();
switch (axis.orient()) {
case 'top':
axisLabel.enter().append('text').attr('class', 'axislabel')
.attr('text-anchor', 'middle')
@ -52,19 +51,17 @@ nv.models.axis = function() {
.attr('x', -scale.range()[0] / 2);
break;
}
axisLabel.exit().remove();
axisLabel
.text(function(d) { return d });
//d3.select(this)
d3.transition(d3.select(this))
.call(axis);
if (orient == 'left' || orient == 'right')
//highlight zero line
if (highlightZero)
d3.select(this)
.selectAll('line.tick')
//.filter(function(d) { return !parseFloat(d) })
.filter(function(d) { return !parseFloat(Math.round(d*100000)/1000000) }) //this is because sometimes the 0 tick is a very small fraction, TODO: think of cleaner technique
.classed('zero', true);
@ -74,43 +71,29 @@ nv.models.axis = function() {
}
//TODO: orient, domain, and range could be rebind's... but then they won't return the chart component
chart.orient = function(_) {
if (!arguments.length) return orient;
orient = _;
axis.orient(_);
return chart;
};
d3.rebind(chart, axis, 'orient', 'ticks', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); //these are also accessible by chart.scale(), but added common ones directly for ease of use
chart.domain = function(_) {
if (!arguments.length) return domain;
//domain = _;
scale.domain(_);
chart.axisLabel = function(_) {
if (!arguments.length) return axisLabelText;
axisLabelText = _;
return chart;
};
}
chart.range = function(_) {
if (!arguments.length) return range;
//range = _;
scale.range(_);
chart.highlightZero = function(_) {
if (!arguments.length) return highlightZero;
highlightZero = _;
return chart;
};
}
chart.scale = function(_) {
if (!arguments.length) return scale;
scale = _;
axis.scale(scale);
return chart;
};
chart.axisLabel = function(_) {
if (!arguments.length) return axisLabelText;
axisLabelText = _;
d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands');
return chart;
}
//d3.rebind(chart, scale, 'domain', 'range', 'rangRoundBands', 'rangeBands'); //would implement, but will break because domain and range won't return chart... will likelu implement later
d3.rebind(chart, axis, 'ticks', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
return chart;
}

@ -170,7 +170,7 @@ nv.models.cumulativeLine = function() {
});
*/
lines.dispatch.on('pointMouseover.tooltip', function(e) {
lines.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -180,7 +180,7 @@ nv.models.cumulativeLine = function() {
});
});
lines.dispatch.on('pointMouseout.tooltip', function(e) {
lines.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});

@ -8,54 +8,54 @@ nv.models.line = function() {
id = Math.floor(Math.random() * 10000), //Create semi-unique ID incase user doesn't select one
getX = function(d) { return d.x }, // accessor to get the x value from a data point
getY = function(d) { return d.y }, // accessor to get the y value from a data point
forceX = [], // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
forceY = [], // List of numbers to Force into the Y scale
interactive = true, // If true, plots a voronoi overlay for advanced point interection
clipEdge = false, // if true, masks lines within x and y scale
clipVoronoi = true, // if true, masks each point with a circle... can turn off to slightly increase performance
xDomain, yDomain; // Used to manually set the x and y domain, good to save time if calculation has already been made
var x = d3.scale.linear(),
y = d3.scale.linear(),
scatter = nv.models.scatter()
clipEdge = false; // if true, masks lines within x and y scale
var scatter = nv.models.scatter()
.size(2.5) // default size
.sizeDomain([2.5]), //set to speed up calculation, needs to be unset if there is a cstom size accessor
x0, y0,
x, y, x0, y0,
timeoutID;
function chart(selection) {
selection.each(function(data) {
var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
data.map(function(d) {
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i) }
})
}),
availableWidth = width - margin.left - margin.right,
var availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom;
//store old scales if they exist
x0 = x0 || x;
y0 = y0 || y;
x0 = x0 || scatter.xScale();
y0 = y0 || scatter.yScale();
x .domain(xDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.x }).concat(forceX)))
.range([0, availableWidth]);
y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y }).concat(forceY)))
.range([availableHeight, 0]);
var wrap = d3.select(this).selectAll('g.d3line').data([data]);
var wrapEnter = wrap.enter().append('g').attr('class', 'd3line');
var defsEnter = wrapEnter.append('defs');
var gEnter = wrapEnter.append('g');
wrapEnter.append('g').attr('class', 'scatterWrap');
var scatterWrap = wrap.select('.scatterWrap').datum(data);
gEnter.append('g').attr('class', 'groups');
scatter
.id(id)
.width(availableWidth)
.height(availableHeight)
d3.transition(scatterWrap).call(scatter);
x = scatter.xScale();
y = scatter.yScale();
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
//TODO: this doesn't remove if turned off after on...
if (clipEdge) {
defsEnter.append('clipPath')
.attr('id', 'edge-clip-' + id)
@ -67,6 +67,8 @@ nv.models.line = function() {
gEnter
.attr('clip-path', 'url(#edge-clip-' + id + ')');
scatterWrap
.attr('clip-path', 'url(#edge-clip-' + id + ')');
}
@ -111,21 +113,6 @@ nv.models.line = function() {
);
scatter
.id(id)
.interactive(interactive)
.width(availableWidth)
.height(availableHeight)
.xDomain(x.domain())
.yDomain(y.domain())
wrapEnter.append('g').attr('class', 'scatterWrap');
var scatterWrap = wrap.select('.scatterWrap').datum(data);
d3.transition(scatterWrap).call(scatter);
//store old scales for use in transitions on update, to animate from old to new positions
x0 = x.copy();
y0 = y.copy();
@ -138,7 +125,7 @@ nv.models.line = function() {
chart.dispatch = scatter.dispatch;
d3.rebind(chart, scatter, 'size');
d3.rebind(chart, scatter, 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius');
chart.margin = function(_) {
if (!arguments.length) return margin;
@ -172,46 +159,9 @@ nv.models.line = function() {
return chart;
};
chart.xDomain = function(_) {
if (!arguments.length) return xDomain;
xDomain = _;
return chart;
};
chart.yDomain = function(_) {
if (!arguments.length) return yDomain;
yDomain = _;
return chart;
};
chart.forceX = function(_) {
if (!arguments.length) return forceX;
forceX = _;
return chart;
};
chart.forceY = function(_) {
if (!arguments.length) return forceY;
forceY = _;
return chart;
};
chart.interactive = function(_) {
if (!arguments.length) return interactive;
interactive = _;
return chart;
};
chart.clipEdge = function(_) {
if (!arguments.length) return clipEdge;
clipEdge = _;
scatter.clipEdge(_);
return chart;
};
chart.clipVoronoi= function(_) {
if (!arguments.length) return clipVoronoi;
clipVoronoi = _;
return chart;
};

@ -91,7 +91,7 @@ nv.models.linePlusBar = function() {
});
lines.dispatch.on('pointMouseover.tooltip', function(e) {
lines.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -101,7 +101,7 @@ nv.models.linePlusBar = function() {
});
});
lines.dispatch.on('pointMouseout.tooltip', function(e) {
lines.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});

@ -215,7 +215,7 @@ nv.models.lineWithFocus = function() {
});
*/
focus.dispatch.on('pointMouseover.tooltip', function(e) {
focus.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -224,7 +224,7 @@ nv.models.lineWithFocus = function() {
pointIndex: e.pointIndex
});
});
focus.dispatch.on('pointMouseout.tooltip', function(e) {
focus.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});

@ -85,7 +85,7 @@ nv.models.lineWithLegend = function() {
});
*/
lines.dispatch.on('pointMouseover.tooltip', function(e) {
lines.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -95,7 +95,7 @@ nv.models.lineWithLegend = function() {
});
});
lines.dispatch.on('pointMouseout.tooltip', function(e) {
lines.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});

@ -126,7 +126,7 @@ nv.models.lineWithLegend = function() {
});
*/
lines.dispatch.on('pointMouseover.tooltip', function(e) {
lines.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -136,7 +136,7 @@ nv.models.lineWithLegend = function() {
});
});
lines.dispatch.on('pointMouseout.tooltip', function(e) {
lines.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});

@ -6,9 +6,12 @@ nv.models.scatter = function() {
height = 500,
color = d3.scale.category20().range(), // array of colors to be used in order
id = Math.floor(Math.random() * 100000), //Create semi-unique ID incase user doesn't selet one
x = d3.scale.linear(),
y = d3.scale.linear(),
z = d3.scale.sqrt(), //sqrt because point size is done by area, not radius
getX = function(d) { return d.x }, // accessor to get the x value from a data point
getY = function(d) { return d.y }, // accessor to get the y value from a data point
getSize = function(d) { return d.size }, // accessor to get the point radius from a data point
getSize = function(d) { return d.size }, // accessor to get the point radius from a data point //TODO: consider renamig size to z
forceX = [], // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
forceY = [], // List of numbers to Force into the Y scale
forceSize = [], // List of numbers to Force into the Size scale
@ -18,24 +21,14 @@ nv.models.scatter = function() {
clipRadius = function() { return 25 }, // function to get the radius for point clips
xDomain, yDomain, sizeDomain; // Used to manually set the x and y domain, good to save time if calculation has already been made
var x = d3.scale.linear(),
y = d3.scale.linear(),
z = d3.scale.sqrt(), //sqrt because point size is done by area, not radius
dispatch = d3.dispatch('pointClick', 'pointMouseover', 'pointMouseout'),//TODO: consider renaming to elementMouseove and elementMouseout for consistency
var dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout'),//TODO: consider renaming to elementMouseove and elementMouseout for consistency
x0, y0, z0,
timeoutID;
function chart(selection) {
selection.each(function(data) {
//var seriesData = data.map(function(d) {
var seriesData = (xDomain && yDomain && sizeDomain) ? [] : // if we know xDomain and yDomain and sizeDomain, no need to calculate.... if Size is constant remember to set sizeDomain to speed up performance
//console.log('recalculating');
data.map(function(d) {
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i), size: getSize(d,i) }
})
}),
availableWidth = width - margin.left - margin.right,
//var seriesData = data.map(function(d) {
var availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom;
//store old scales if they exist
@ -46,8 +39,6 @@ nv.models.scatter = function() {
//add series index to each data point for reference
data = data.map(function(series, i) {
series.values = series.values.map(function(point) {
//point.label = series.label;
//point.color = series.color;
point.series = i;
return point;
});
@ -55,7 +46,16 @@ nv.models.scatter = function() {
});
// slight remap of the data for use in calculating the scales domains
var seriesData = (xDomain && yDomain && sizeDomain) ? [] : // if we know xDomain and yDomain and sizeDomain, no need to calculate.... if Size is constant remember to set sizeDomain to speed up performance
data.map(function(d) {
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i), size: getSize(d,i) }
})
});
//TODO: figure out the best way to deal with scales with equal MIN and MAX
//TODO: think of a good way to re-use scales
x .domain(xDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.x }).concat(forceX)))
.range([0, availableWidth]);
@ -146,7 +146,7 @@ nv.models.scatter = function() {
var series = data[d.series],
point = series.values[d.point];
dispatch.pointClick({
dispatch.elementClick({
point: point,
series:series,
pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
@ -158,7 +158,7 @@ nv.models.scatter = function() {
var series = data[d.series],
point = series.values[d.point];
dispatch.pointMouseover({
dispatch.elementMouseover({
point: point,
series:series,
pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
@ -167,7 +167,7 @@ nv.models.scatter = function() {
});
})
.on('mouseout', function(d, i) {
dispatch.pointMouseout({
dispatch.elementMouseout({
point: data[d.series].values[d.point],
series: data[d.series],
seriesIndex: d.series,
@ -175,12 +175,12 @@ nv.models.scatter = function() {
});
});
dispatch.on('pointMouseover.point', function(d) {
dispatch.on('elementMouseover.point', function(d) {
wrap.select('.series-' + d.seriesIndex + ' .point-' + d.pointIndex)
.classed('hover', true);
});
dispatch.on('pointMouseout.point', function(d) {
dispatch.on('elementMouseout.point', function(d) {
wrap.select('.series-' + d.seriesIndex + ' circle.point-' + d.pointIndex)
.classed('hover', false);
});
@ -201,7 +201,7 @@ nv.models.scatter = function() {
.remove();
groups
.attr('class', function(d,i) { return 'group series-' + i })
.classed('hover', function(d) { return d.hover && !d.disabled });
.classed('hover', function(d) { return d.hover });
d3.transition(groups)
.style('fill', function(d,i) { return color[i % 20] })
.style('stroke', function(d,i) { return color[i % 20] })
@ -281,6 +281,24 @@ nv.models.scatter = function() {
return chart;
};
chart.xScale = function(_) {
if (!arguments.length) return x;
x = _;
return chart;
};
chart.yScale = function(_) {
if (!arguments.length) return y;
y = _;
return chart;
};
chart.zScale = function(_) {
if (!arguments.length) return z;
z = _;
return chart;
};
chart.xDomain = function(_) {
if (!arguments.length) return xDomain;
xDomain = _;

@ -190,7 +190,7 @@ nv.models.scatterWithLegend = function() {
*/
scatter.dispatch.on('pointMouseover.tooltip', function(e) {
scatter.dispatch.on('elementMouseover.tooltip', function(e) {
dispatch.tooltipShow({
point: e.point,
series: e.series,
@ -200,18 +200,18 @@ nv.models.scatterWithLegend = function() {
});
});
scatter.dispatch.on('pointMouseout.tooltip', function(e) {
scatter.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});
scatter.dispatch.on('pointMouseover.dist', function(d) {
scatter.dispatch.on('elementMouseover.dist', function(d) {
scatterWrap.select('.series-' + d.seriesIndex + ' .distX-' + d.pointIndex)
.attr('y1', d.pos[1]);
scatterWrap.select('.series-' + d.seriesIndex + ' .distY-' + d.pointIndex)
.attr('x1', d.pos[0]);
});
scatter.dispatch.on('pointMouseout.dist', function(d) {
scatter.dispatch.on('elementMouseout.dist', function(d) {
scatterWrap.select('.series-' + d.seriesIndex + ' .distX-' + d.pointIndex)
.attr('y1', y.range()[0]);
scatterWrap.select('.series-' + d.seriesIndex + ' .distY-' + d.pointIndex)

@ -5,12 +5,12 @@ nv.models.stackedArea = function() {
width = 960,
height = 500,
color = d3.scale.category20().range(), // array of colors to be used in order
id = Math.floor(Math.random() * 100000), //Create semi-unique ID incase user doesn't selet one
getX = function(d) { return d.x }, // accessor to get the x value from a data point
getY = function(d) { return d.y }, // accessor to get the y value from a data point
style = 'stack',
offset = 'zero',
order = 'default',
interactive = true, // If true, plots a voronoi overlay for advanced point interection
clipEdge = false, // if true, masks lines within x and y scale
xDomain, yDomain; // Used to manually set the x and y domain, good to save time if calculation has already been made
@ -29,23 +29,20 @@ nv.models.stackedArea = function() {
var scatter= nv.models.scatter()
.size(2.2) // default size
.sizeDomain([2.5]), //set to speed up calculation, needs to be unset if there is a cstom size accessor
x = d3.scale.linear(),
y = d3.scale.linear(),
x2 = d3.scale.linear(),
y2 = d3.scale.linear(),
dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'areaClick', 'areaMouseover', 'areaMouseout');
function chart(selection) {
selection.each(function(data) {
// Need to leave data alone to switch between stacked, stream, and expanded
var dataCopy = JSON.parse(JSON.stringify(data)),
seriesData = dataCopy.map(function(d) { //TODO: series data is barely used, can probably remove this pretty easily
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i) }
})
}),
availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom;
//TODO: deal with negative stacked charts
//compute the data based on offset and order (calc's y0 for every point)
dataCopy = d3.layout.stack()
.offset(offset)
@ -55,15 +52,22 @@ nv.models.stackedArea = function() {
(dataCopy);
x .domain(xDomain || d3.extent(d3.merge(seriesData), function(d) { return d.x } ))
/*
var seriesData = dataCopy.map(function(d) { //TODO: series data is barely used, can probably remove this pretty easily
return d.values.map(function(d,i) {
return { x: getX(d,i), y: getY(d,i) }
})
});
x2 .domain(xDomain || d3.extent(d3.merge(seriesData), function(d) { return d.x } ))
.range([0, availableWidth]);
//TODO: deal with negative stacked charts
y .domain(yDomain || [0, d3.max(dataCopy, function(d) { //TODO; if dataCopy not fed {x, y} (custom getX or getY), this will probably cause an error
y2 .domain(yDomain || [0, d3.max(dataCopy, function(d) { //TODO; if dataCopy not fed {x, y} (custom getX or getY), this will probably cause an error
return d3.max(d.values, function(d) { return d.y0 + d.y })
}) ])
.range([availableHeight, 0]);
*/
var wrap = d3.select(this).selectAll('g.d3stackedarea').data([dataCopy]);
@ -75,6 +79,31 @@ nv.models.stackedArea = function() {
gEnter.append('g').attr('class', 'areaWrap');
scatter
.width(availableWidth)
.height(availableHeight)
.x(getX)
.y(function(d) { return d.y + d.y0 }) // TODO: allow for getY to be other than d.y
.forceY([0])
.color(data.map(function(d,i) {
return d.color || color[i % 20];
}).filter(function(d,i) { return !data[i].disabled }));
gEnter.append('g').attr('class', 'scatterWrap');
var scatterWrap= g.select('.scatterWrap')
.datum(dataCopy.filter(function(d) { return !d.disabled }))
d3.transition(scatterWrap).call(scatter);
x = scatter.xScale();
y = scatter.yScale();
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@ -93,26 +122,6 @@ nv.models.stackedArea = function() {
//TODO: need to also remove area Hover/Click to turn off interactive
if (interactive) {
scatter
.width(availableWidth)
.height(availableHeight)
.xDomain(x.domain())
.yDomain(y.domain())
.x(getX)
.y(function(d) { return d.y + d.y0 }) // TODO: allow for getY to be other than d.y
.color(data.map(function(d,i) {
return d.color || color[i % 20];
}).filter(function(d,i) { return !data[i].disabled }));
gEnter.append('g').attr('class', 'scatterWrap');
var scatterWrap= g.select('.scatterWrap')
.datum(dataCopy.filter(function(d) { return !d.disabled }))
d3.transition(scatterWrap).call(scatter);
}
var area = d3.svg.area()
.x(function(d,i) { return x(getX(d,i)) })
@ -173,13 +182,13 @@ nv.models.stackedArea = function() {
//TODO: these disptach handlers don't need to be called everytime the chart
// is called, but 'g' is only in this scope... so need to rethink.
scatter.dispatch.on('pointClick.area', function(e) {
scatter.dispatch.on('elementClick.area', function(e) {
dispatch.areaClick(e);
})
scatter.dispatch.on('pointMouseover.area', function(e) {
scatter.dispatch.on('elementMouseover.area', function(e) {
g.select('.area-' + e.seriesIndex).classed('hover', true);
});
scatter.dispatch.on('pointMouseout.area', function(e) {
scatter.dispatch.on('elementMouseout.area', function(e) {
g.select('.area-' + e.seriesIndex).classed('hover', false);
});
@ -189,6 +198,12 @@ nv.models.stackedArea = function() {
return chart;
}
chart.dispatch = dispatch;
chart.scatter = scatter;
d3.rebind(chart, scatter, 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius');
chart.x = function(_) {
if (!arguments.length) return getX;
getX = d3.functor(_);
@ -219,12 +234,6 @@ nv.models.stackedArea = function() {
return chart;
};
chart.interactive = function(_) {
if (!arguments.length) return interactive;
interactive = _;
return chart;
};
chart.clipEdge = function(_) {
if (!arguments.length) return clipEdge;
clipEdge = _;
@ -272,16 +281,14 @@ nv.models.stackedArea = function() {
return chart;
};
chart.dispatch = dispatch;
chart.scatter = scatter;
scatter.dispatch.on('pointMouseover.tooltip', function(e) {
scatter.dispatch.on('elementMouseover.tooltip', function(e) {
e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
dispatch.tooltipShow(e);
});
scatter.dispatch.on('pointMouseout.tooltip', function(e) {
scatter.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});

@ -225,7 +225,7 @@ nv.models.stackedAreaWithLegend = function() {
chart.dispatch = dispatch;
d3.rebind(chart, stacked, 'interactive');
d3.rebind(chart, stacked, 'interactive', 'clipEdge');
chart.x = function(_) {
if (!arguments.length) return getX;

Loading…
Cancel
Save