Stepped Interpolation Implementation

master
johnkiernander 2014-04-22 01:38:41 +01:00
parent 41955de85c
commit 1b1b8231c9
5 changed files with 434 additions and 341 deletions

257
dist/dimple.v1.2.0.js vendored
View File

@ -2643,6 +2643,11 @@ var dimple = {
basePoints,
basePoint,
cat,
catVal,
group,
p,
b,
l,
lastAngle,
catCoord,
valCoord,
@ -2680,6 +2685,9 @@ var dimple = {
.y(function (d) { return (series.y._hasCategories() || !originProperty ? d.y : series.y[originProperty]); })
.interpolate(inter);
},
sortByVal = function (a, b) {
return parseFloat(a) - parseFloat(b);
},
sortByX = function (a, b) {
return parseFloat(a.x) - parseFloat(b.x);
},
@ -2750,7 +2758,9 @@ var dimple = {
points: [],
area: {},
entry: {},
exit: {}
exit: {},
// Group refers to groupings along the category axis. If there are groupings it will be recorded, otherwise all is used as a default
group: (catCoord && data[i][catCoord + "Field"] && data[i][catCoord + "Field"].length >= 2 ? data[i][catCoord + "Field"][0] : "All")
});
}
// Add this row to the relevant data
@ -2779,7 +2789,32 @@ var dimple = {
// if there is a category axis, add the points to a distinct set. Set these to use the origin value
// this will be updated with the last value in each case as we build the areas
if (catCoord) {
catPoints[areaData[i].points[areaData[i].points.length - 1][catCoord]] = series[valCoord]._previousOrigin;
if (!catPoints[areaData[i].group]) {
catPoints[areaData[i].group] = {};
}
catPoints[areaData[i].group][areaData[i].points[areaData[i].points.length - 1][catCoord]] = series[valCoord]._previousOrigin;
}
}
points = areaData[i].points;
// If this is a step interpolation we need to add in some extra points to the category axis
// This is a little tricky but we need to add a new point duplicating the last category value. In order
// to place the point we need to calculate the gap between the last x and the penultimate x and apply that
// gap again.
if (series.interpolation === "step" && points.length > 1 && catCoord) {
if (series.x._hasCategories()) {
points.push({
x : 2 * points[points.length - 1].x - points[points.length - 2].x,
y : points[points.length - 1].y
});
catPoints[areaData[i].group][points[points.length - 1][catCoord]] = series[valCoord]._previousOrigin;
} else if (series.y._hasCategories()) {
points = [{
x : points[0].x,
y : 2 * points[0].y - points[1].y
}].concat(points);
catPoints[areaData[i].group][points[0][catCoord]] = series[valCoord]._previousOrigin;
// The prepend above breaks the reference so it needs to be reapplied here.
areaData[i].points = points;
}
}
}
@ -2787,47 +2822,54 @@ var dimple = {
// catPoints needs to be lookup, but also accessed sequentially so we need to create an array of keys
for (cat in catPoints) {
if (catPoints.hasOwnProperty(cat)) {
allPoints.push(parseFloat(cat));
allPoints[cat] = [];
for (catVal in catPoints[cat]) {
if (catPoints[cat].hasOwnProperty(catVal)) {
allPoints[cat].push(parseFloat(catVal));
}
}
// Sort the points as integers
allPoints[cat].sort(sortByVal);
}
}
// Sort the points as integers
allPoints.sort(function (a, b) { return parseFloat(a) - parseFloat(b); });
// Create the areas
for (i = 0; i < areaData.length; i += 1) {
points = areaData[i].points;
group = areaData[i].group;
basePoints = [];
finalPointArray = [];
// If this should have colour gradients, add them
if (graded) {
dimple._addGradient(areaData[i].key, "fill-area-gradient-" + areaData[i].keyString, (series.x._hasCategories() ? series.x : series.y), data, chart, duration, "fill");
}
// All points will only be populated if there is a category axis
if (allPoints && allPoints.length > 0) {
if (allPoints[group] && allPoints[group].length > 0) {
// Iterate the point array because we need to fill in zero points for missing ones, otherwise the areas
// will cross where an upper area has no value and a lower value has a spike Issue #7
for (j = 0, k = 0; j < allPoints.length; j += 1) {
for (j = 0, k = 0; j < allPoints[group].length; j += 1) {
// We are only interested in points between the first and last point of this areas data (i.e. don't fill ends - important
// for grouped area charts)
if (allPoints[j] >= points[0][catCoord] && allPoints[j] <= points[points.length - 1][catCoord]) {
// for grouped area charts). We have to use a strange criteria here. If there are no group gaps on a grouped area
// chart the end point of one series will clash with the start point of another, therefore we have to ignore fill-in's within
// a couple of pixels of the start and end points
if (allPoints[group][j] >= points[0][catCoord] && allPoints[group][j] <= points[points.length - 1][catCoord]) {
// Get a base point, this needs to go on the base points array as well as filling in gaps in the point array.
// Create a point using the coordinate on the category axis and the last recorded value
// position from the dictionary
basePoint = {};
basePoint[catCoord] = allPoints[j];
basePoint[valCoord] = catPoints[allPoints[j]];
basePoint[catCoord] = allPoints[group][j];
basePoint[valCoord] = catPoints[group][allPoints[group][j]];
// add the base point
basePoints.push(basePoint);
// handle missing points
if (points[k][catCoord] > allPoints[j]) {
if (points[k][catCoord] > allPoints[group][j]) {
// If there is a missing point we need to in fill
finalPointArray.push(basePoint);
} else {
// They must be the same
finalPointArray.push(points[k]);
// Use this to update the dictionary to the new value coordinate
catPoints[allPoints[j]] = points[k][valCoord];
catPoints[areaData[i].group][allPoints[group][j]] = points[k][valCoord];
k += 1;
}
}
@ -2863,88 +2905,30 @@ var dimple = {
}
}
// The final array of points for the entire outskirts of the area
finalPointArray = finalPointArray.concat(basePoints.reverse()).concat(finalPointArray[0]);
// Reverse the base points so that they are in the correct order for the path
basePoints = basePoints.reverse();
// Get the points that this area will appear
areaData[i].entry = getArea(interpolation, "_previousOrigin")(finalPointArray);
areaData[i].update = getArea(interpolation)(finalPointArray);
areaData[i].exit = getArea(interpolation, "_origin")(finalPointArray);
p = getArea(interpolation, "_previousOrigin")(finalPointArray);
b = getArea((interpolation === "step-after" ? "step-before" : (interpolation === "step-before" ? "step-after" : interpolation)), "_previousOrigin")(basePoints);
l = getArea("linear", "_previousOrigin")(finalPointArray);
areaData[i].entry = p + "L" + b.substring(1) + "L" + l.substring(1, l.indexOf("L"));
p = getArea(interpolation)(finalPointArray);
b = getArea(interpolation === "step-after" ? "step-before" : (interpolation === "step-before" ? "step-after" : interpolation))(basePoints);
l = getArea("linear")(finalPointArray);
areaData[i].update = p + "L" + b.substring(1) + "L" + l.substring(1, l.indexOf("L"));
p = getArea(interpolation, "_origin")(finalPointArray);
b = getArea((interpolation === "step-after" ? "step-before" : (interpolation === "step-before" ? "step-after" : interpolation)), "_origin")(basePoints);
l = getArea("linear", "_origin")(finalPointArray);
areaData[i].exit = p + "L" + b.substring(1) + "L" + l.substring(1, l.indexOf("L"));
// Add the color in this loop, it can't be done during initialisation of the row because
// the areas should be ordered first (to ensure standard distribution of colors
areaData[i].color = chart.getColor(areaData[i].key.length > 0 ? areaData[i].key[areaData[i].key.length - 1] : "All");
}
// // Create a set of area data grouped by the aggregation field
// for (i = 0; i < areaData.length; i += 1) {
//
// // Sort the points so that areas are connected in the correct order
// areaData[i].data.sort(dimple._getSeriesSortPredicate(chart, series, orderedSeriesArray));
//
// // If this should have colour gradients, add them
// if (graded) {
// dimple._addGradient(areaData[i].key, "fill-area-gradient-" + areaData[i].keyString, (series.x._hasCategories() ? series.x : series.y), data, chart, duration, "fill");
// }
//
// // Clone the data before adding elements
// dataClone = [].concat(areaData[i].data);
//
// // If this is a custom dimple step area duplicate the last datum so that the final step is completed
// if (series.interpolation === "step") {
// if (series.x._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = dataClone.concat(JSON.parse(JSON.stringify(dataClone[dataClone.length - 1])));
// dataClone[dataClone.length - 1].cx = "";
// dataClone[dataClone.length - 1].x = "";
// }
// if (series.y._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = [JSON.parse(JSON.stringify(dataClone[0]))].concat(dataClone);
// dataClone[0].cy = "";
// dataClone[0].y = "";
// }
// }
//
// // Add zero baseline for this row
// if (series.x._hasCategories() && series.y._hasMeasure()) {
// for (j = dataClone.length - 1; j >= 0; j -= 1) {
// areaData[i].baseData.push({
// x : dataClone[j].x,
// cx : dataClone[j].cx,
// xOffset : dataClone[j].xOffset,
// width : dataClone[j].width,
// y: (i === 0 ? 0 : areaData[i - 1].data[j].y),
// cy : (i === 0 ? 0 : areaData[i - 1].data[j].cy),
// yOffset: (i === 0 ? 0 : areaData[i - 1].data[j].yOffset),
// height: (i === 0 ? 0 : areaData[i - 1].data[j].height)
// });
// }
// } else if (series.y._hasCategories() && series.x._hasMeasure()) {
// for (j = dataClone.length - 1; j >= 0; j -= 1) {
// areaData[i].baseData.push({
// x : (i === 0 ? 0 : areaData[i - 1].data[j].x),
// cx : (i === 0 ? 0 : areaData[i - 1].data[j].cx),
// xOffset : (i === 0 ? 0 : areaData[i - 1].data[j].xOffset),
// width : (i === 0 ? 0 : areaData[i - 1].data[j].width),
// y: dataClone[j].y,
// cy : dataClone[j].cy,
// yOffset: dataClone[j].yOffset,
// height: dataClone[j].height
// });
// }
// }
//
// // Get the points that this area will appear
// areaData[i].entry = getArea(interpolation, "_previousOrigin")(areaData[i].baseData.concat(dataClone));
// areaData[i].update = getArea(interpolation)(areaData[i].baseData.concat(dataClone));
// areaData[i].exit = getArea(interpolation, "_origin")(areaData[i].baseData.concat(dataClone));
//
// // Add the color in this loop, it can't be done during initialisation of the row because
// // the areas should be ordered first (to ensure standard distribution of colors
// areaData[i].color = chart.getColor(areaData[i].key.length > 0 ? areaData[i].key[areaData[i].key.length - 1] : "All");
//
// }
if (chart._tooltipGroup !== null && chart._tooltipGroup !== undefined) {
chart._tooltipGroup.remove();
@ -2971,13 +2955,12 @@ var dimple = {
// Apply formats optionally
if (!chart.noFormats) {
this.attr("opacity", function (d) { return (graded ? 1 : d.color.opacity); })
.attr("fill", function (d) { return (graded ? "url(#fill-line-gradient-" + d.keyString + ")" : d.color.fill); })
.attr("stroke", function (d) { return (graded ? "url(#stroke-line-gradient-" + d.keyString + ")" : d.color.stroke); })
.attr("fill", function (d) { return (graded ? "url(#fill-area-gradient-" + d.keyString + ")" : d.color.fill); })
.attr("stroke", function (d) { return (graded ? "url(#stroke-area-gradient-" + d.keyString + ")" : d.color.stroke); })
.attr("stroke-width", series.lineWeight);
}
})
.each(drawMarkers);
// Update
updated = dimple._handleTransition(theseShapes, duration)
.attr("d", function (d) { return d.update; })
@ -3222,6 +3205,7 @@ var dimple = {
interpolation,
graded = false,
i,
j,
k,
key,
keyString,
@ -3229,7 +3213,6 @@ var dimple = {
updated,
removed,
orderedSeriesArray,
dataClone,
onEnter = function () {
return function (e, shape, chart, series) {
d3.select(shape).style("opacity", 1);
@ -3254,14 +3237,33 @@ var dimple = {
} else {
val = dimple._helpers["c" + position](datum, chart, series);
}
return val;
// Remove long decimals from the coordinates as this fills the dom up with noise and makes matching below less likely to work. It
// shouldn't really matter but positioning to < 0.1 pixel is pretty pointless anyway.
return parseFloat(val.toFixed(1));
},
getLine = function (inter, originProperty) {
return d3.svg.line()
.x(function (d) { return (series.x._hasCategories() || !originProperty ? coord("x", d) : series.x[originProperty]); })
.y(function (d) { return (series.y._hasCategories() || !originProperty ? coord("y", d) : series.y[originProperty]); })
.x(function (d) { return (series.x._hasCategories() || !originProperty ? d.x : series.x[originProperty]); })
.y(function (d) { return (series.y._hasCategories() || !originProperty ? d.y : series.y[originProperty]); })
.interpolate(inter);
};
// coord = function (position, datum) {
// var val;
// if (series.interpolation === "step" && series[position]._hasCategories()) {
// series.barGap = 0;
// series.clusterBarGap = 0;
// val = dimple._helpers[position](datum, chart, series) + (position === "y" ? dimple._helpers.height(datum, chart, series) : 0);
// } else {
// val = dimple._helpers["c" + position](datum, chart, series);
// }
// return parseFloat(val).toFixed(1);
// },
// getLine = function (inter, originProperty) {
// return d3.svg.line()
// .x(function (d) { return (series.x._hasCategories() || !originProperty ? coord("x", d) : series.x[originProperty]); })
// .y(function (d) { return (series.y._hasCategories() || !originProperty ? coord("y", d) : series.y[originProperty]); })
// .interpolate(inter);
// };
// Handle the special interpolation handling for step
interpolation = (series.interpolation === "step" ? "step-after" : series.interpolation);
@ -3297,8 +3299,10 @@ var dimple = {
keyString: keyString,
color: "white",
data: [],
points: [],
line: {},
entryExit: {}
entry: {},
exit: {}
});
}
// Add this row to the relevant data
@ -3321,28 +3325,55 @@ var dimple = {
if (graded) {
dimple._addGradient(lineData[i].key, "fill-line-gradient-" + lineData[i].keyString, (series.x._hasCategories() ? series.x : series.y), data, chart, duration, "fill");
}
// Clone the data before adding elements
dataClone = [].concat(lineData[i].data);
// If this is a custom dimple step line duplicate the last datum so that the final step is completed
if (series.interpolation === "step") {
// Get points here, this is so that as well as drawing the line with them, we can also
// use them for the baseline
for (j = 0; j < lineData[i].data.length; j += 1) {
lineData[i].points.push({
x: coord("x", lineData[i].data[j]),
y: coord("y", lineData[i].data[j])
});
}
// If this is a step interpolation we need to add in some extra points to the category axis
// This is a little tricky but we need to add a new point duplicating the last category value. In order
// to place the point we need to calculate the gap between the last x and the penultimate x and apply that
// gap again.
if (series.interpolation === "step" && lineData[i].points.length > 1) {
if (series.x._hasCategories()) {
// Clone the last row and duplicate it.
dataClone = dataClone.concat(JSON.parse(JSON.stringify(dataClone[dataClone.length - 1])));
dataClone[dataClone.length - 1].cx = "";
dataClone[dataClone.length - 1].x = "";
}
if (series.y._hasCategories()) {
// Clone the last row and duplicate it.
dataClone = [JSON.parse(JSON.stringify(dataClone[0]))].concat(dataClone);
dataClone[0].cy = "";
dataClone[0].y = "";
lineData[i].points.push({
x : 2 * lineData[i].points[lineData[i].points.length - 1].x - lineData[i].points[lineData[i].points.length - 2].x,
y : lineData[i].points[lineData[i].points.length - 1].y
});
} else if (series.y._hasCategories()) {
lineData[i].points = [{
x : lineData[i].points[0].x,
y : 2 * lineData[i].points[0].y - lineData[i].points[1].y
}].concat(lineData[i].points);
}
}
// // Clone the data before adding elements
// dataClone = [].concat(lineData[i].data);
// // If this is a custom dimple step line duplicate the last datum so that the final step is completed
// if (series.interpolation === "step") {
// if (series.x._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = dataClone.concat(JSON.parse(JSON.stringify(dataClone[dataClone.length - 1])));
// dataClone[dataClone.length - 1].cx = "";
// dataClone[dataClone.length - 1].x = "";
// }
// if (series.y._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = [JSON.parse(JSON.stringify(dataClone[0]))].concat(dataClone);
// dataClone[0].cy = "";
// dataClone[0].y = "";
// }
// }
// Get the points that this line will appear
lineData[i].entry = getLine(interpolation, "_previousOrigin")(dataClone);
lineData[i].update = getLine(interpolation)(dataClone);
lineData[i].exit = getLine(interpolation, "_origin")(dataClone);
lineData[i].entry = getLine(interpolation, "_previousOrigin")(lineData[i].points);
lineData[i].update = getLine(interpolation)(lineData[i].points);
lineData[i].exit = getLine(interpolation, "_origin")(lineData[i].points);
// Add the color in this loop, it can't be done during initialisation of the row because
// the lines should be ordered first (to ensure standard distribution of colors

File diff suppressed because one or more lines are too long

View File

@ -35,6 +35,11 @@
basePoints,
basePoint,
cat,
catVal,
group,
p,
b,
l,
lastAngle,
catCoord,
valCoord,
@ -72,6 +77,9 @@
.y(function (d) { return (series.y._hasCategories() || !originProperty ? d.y : series.y[originProperty]); })
.interpolate(inter);
},
sortByVal = function (a, b) {
return parseFloat(a) - parseFloat(b);
},
sortByX = function (a, b) {
return parseFloat(a.x) - parseFloat(b.x);
},
@ -142,7 +150,9 @@
points: [],
area: {},
entry: {},
exit: {}
exit: {},
// Group refers to groupings along the category axis. If there are groupings it will be recorded, otherwise all is used as a default
group: (catCoord && data[i][catCoord + "Field"] && data[i][catCoord + "Field"].length >= 2 ? data[i][catCoord + "Field"][0] : "All")
});
}
// Add this row to the relevant data
@ -171,7 +181,32 @@
// if there is a category axis, add the points to a distinct set. Set these to use the origin value
// this will be updated with the last value in each case as we build the areas
if (catCoord) {
catPoints[areaData[i].points[areaData[i].points.length - 1][catCoord]] = series[valCoord]._previousOrigin;
if (!catPoints[areaData[i].group]) {
catPoints[areaData[i].group] = {};
}
catPoints[areaData[i].group][areaData[i].points[areaData[i].points.length - 1][catCoord]] = series[valCoord]._previousOrigin;
}
}
points = areaData[i].points;
// If this is a step interpolation we need to add in some extra points to the category axis
// This is a little tricky but we need to add a new point duplicating the last category value. In order
// to place the point we need to calculate the gap between the last x and the penultimate x and apply that
// gap again.
if (series.interpolation === "step" && points.length > 1 && catCoord) {
if (series.x._hasCategories()) {
points.push({
x : 2 * points[points.length - 1].x - points[points.length - 2].x,
y : points[points.length - 1].y
});
catPoints[areaData[i].group][points[points.length - 1][catCoord]] = series[valCoord]._previousOrigin;
} else if (series.y._hasCategories()) {
points = [{
x : points[0].x,
y : 2 * points[0].y - points[1].y
}].concat(points);
catPoints[areaData[i].group][points[0][catCoord]] = series[valCoord]._previousOrigin;
// The prepend above breaks the reference so it needs to be reapplied here.
areaData[i].points = points;
}
}
}
@ -179,47 +214,54 @@
// catPoints needs to be lookup, but also accessed sequentially so we need to create an array of keys
for (cat in catPoints) {
if (catPoints.hasOwnProperty(cat)) {
allPoints.push(parseFloat(cat));
allPoints[cat] = [];
for (catVal in catPoints[cat]) {
if (catPoints[cat].hasOwnProperty(catVal)) {
allPoints[cat].push(parseFloat(catVal));
}
}
// Sort the points as integers
allPoints[cat].sort(sortByVal);
}
}
// Sort the points as integers
allPoints.sort(function (a, b) { return parseFloat(a) - parseFloat(b); });
// Create the areas
for (i = 0; i < areaData.length; i += 1) {
points = areaData[i].points;
group = areaData[i].group;
basePoints = [];
finalPointArray = [];
// If this should have colour gradients, add them
if (graded) {
dimple._addGradient(areaData[i].key, "fill-area-gradient-" + areaData[i].keyString, (series.x._hasCategories() ? series.x : series.y), data, chart, duration, "fill");
}
// All points will only be populated if there is a category axis
if (allPoints && allPoints.length > 0) {
if (allPoints[group] && allPoints[group].length > 0) {
// Iterate the point array because we need to fill in zero points for missing ones, otherwise the areas
// will cross where an upper area has no value and a lower value has a spike Issue #7
for (j = 0, k = 0; j < allPoints.length; j += 1) {
for (j = 0, k = 0; j < allPoints[group].length; j += 1) {
// We are only interested in points between the first and last point of this areas data (i.e. don't fill ends - important
// for grouped area charts)
if (allPoints[j] >= points[0][catCoord] && allPoints[j] <= points[points.length - 1][catCoord]) {
// for grouped area charts). We have to use a strange criteria here. If there are no group gaps on a grouped area
// chart the end point of one series will clash with the start point of another, therefore we have to ignore fill-in's within
// a couple of pixels of the start and end points
if (allPoints[group][j] >= points[0][catCoord] && allPoints[group][j] <= points[points.length - 1][catCoord]) {
// Get a base point, this needs to go on the base points array as well as filling in gaps in the point array.
// Create a point using the coordinate on the category axis and the last recorded value
// position from the dictionary
basePoint = {};
basePoint[catCoord] = allPoints[j];
basePoint[valCoord] = catPoints[allPoints[j]];
basePoint[catCoord] = allPoints[group][j];
basePoint[valCoord] = catPoints[group][allPoints[group][j]];
// add the base point
basePoints.push(basePoint);
// handle missing points
if (points[k][catCoord] > allPoints[j]) {
if (points[k][catCoord] > allPoints[group][j]) {
// If there is a missing point we need to in fill
finalPointArray.push(basePoint);
} else {
// They must be the same
finalPointArray.push(points[k]);
// Use this to update the dictionary to the new value coordinate
catPoints[allPoints[j]] = points[k][valCoord];
catPoints[areaData[i].group][allPoints[group][j]] = points[k][valCoord];
k += 1;
}
}
@ -255,88 +297,30 @@
}
}
// The final array of points for the entire outskirts of the area
finalPointArray = finalPointArray.concat(basePoints.reverse()).concat(finalPointArray[0]);
// Reverse the base points so that they are in the correct order for the path
basePoints = basePoints.reverse();
// Get the points that this area will appear
areaData[i].entry = getArea(interpolation, "_previousOrigin")(finalPointArray);
areaData[i].update = getArea(interpolation)(finalPointArray);
areaData[i].exit = getArea(interpolation, "_origin")(finalPointArray);
p = getArea(interpolation, "_previousOrigin")(finalPointArray);
b = getArea((interpolation === "step-after" ? "step-before" : (interpolation === "step-before" ? "step-after" : interpolation)), "_previousOrigin")(basePoints);
l = getArea("linear", "_previousOrigin")(finalPointArray);
areaData[i].entry = p + "L" + b.substring(1) + "L" + l.substring(1, l.indexOf("L"));
p = getArea(interpolation)(finalPointArray);
b = getArea(interpolation === "step-after" ? "step-before" : (interpolation === "step-before" ? "step-after" : interpolation))(basePoints);
l = getArea("linear")(finalPointArray);
areaData[i].update = p + "L" + b.substring(1) + "L" + l.substring(1, l.indexOf("L"));
p = getArea(interpolation, "_origin")(finalPointArray);
b = getArea((interpolation === "step-after" ? "step-before" : (interpolation === "step-before" ? "step-after" : interpolation)), "_origin")(basePoints);
l = getArea("linear", "_origin")(finalPointArray);
areaData[i].exit = p + "L" + b.substring(1) + "L" + l.substring(1, l.indexOf("L"));
// Add the color in this loop, it can't be done during initialisation of the row because
// the areas should be ordered first (to ensure standard distribution of colors
areaData[i].color = chart.getColor(areaData[i].key.length > 0 ? areaData[i].key[areaData[i].key.length - 1] : "All");
}
// // Create a set of area data grouped by the aggregation field
// for (i = 0; i < areaData.length; i += 1) {
//
// // Sort the points so that areas are connected in the correct order
// areaData[i].data.sort(dimple._getSeriesSortPredicate(chart, series, orderedSeriesArray));
//
// // If this should have colour gradients, add them
// if (graded) {
// dimple._addGradient(areaData[i].key, "fill-area-gradient-" + areaData[i].keyString, (series.x._hasCategories() ? series.x : series.y), data, chart, duration, "fill");
// }
//
// // Clone the data before adding elements
// dataClone = [].concat(areaData[i].data);
//
// // If this is a custom dimple step area duplicate the last datum so that the final step is completed
// if (series.interpolation === "step") {
// if (series.x._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = dataClone.concat(JSON.parse(JSON.stringify(dataClone[dataClone.length - 1])));
// dataClone[dataClone.length - 1].cx = "";
// dataClone[dataClone.length - 1].x = "";
// }
// if (series.y._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = [JSON.parse(JSON.stringify(dataClone[0]))].concat(dataClone);
// dataClone[0].cy = "";
// dataClone[0].y = "";
// }
// }
//
// // Add zero baseline for this row
// if (series.x._hasCategories() && series.y._hasMeasure()) {
// for (j = dataClone.length - 1; j >= 0; j -= 1) {
// areaData[i].baseData.push({
// x : dataClone[j].x,
// cx : dataClone[j].cx,
// xOffset : dataClone[j].xOffset,
// width : dataClone[j].width,
// y: (i === 0 ? 0 : areaData[i - 1].data[j].y),
// cy : (i === 0 ? 0 : areaData[i - 1].data[j].cy),
// yOffset: (i === 0 ? 0 : areaData[i - 1].data[j].yOffset),
// height: (i === 0 ? 0 : areaData[i - 1].data[j].height)
// });
// }
// } else if (series.y._hasCategories() && series.x._hasMeasure()) {
// for (j = dataClone.length - 1; j >= 0; j -= 1) {
// areaData[i].baseData.push({
// x : (i === 0 ? 0 : areaData[i - 1].data[j].x),
// cx : (i === 0 ? 0 : areaData[i - 1].data[j].cx),
// xOffset : (i === 0 ? 0 : areaData[i - 1].data[j].xOffset),
// width : (i === 0 ? 0 : areaData[i - 1].data[j].width),
// y: dataClone[j].y,
// cy : dataClone[j].cy,
// yOffset: dataClone[j].yOffset,
// height: dataClone[j].height
// });
// }
// }
//
// // Get the points that this area will appear
// areaData[i].entry = getArea(interpolation, "_previousOrigin")(areaData[i].baseData.concat(dataClone));
// areaData[i].update = getArea(interpolation)(areaData[i].baseData.concat(dataClone));
// areaData[i].exit = getArea(interpolation, "_origin")(areaData[i].baseData.concat(dataClone));
//
// // Add the color in this loop, it can't be done during initialisation of the row because
// // the areas should be ordered first (to ensure standard distribution of colors
// areaData[i].color = chart.getColor(areaData[i].key.length > 0 ? areaData[i].key[areaData[i].key.length - 1] : "All");
//
// }
if (chart._tooltipGroup !== null && chart._tooltipGroup !== undefined) {
chart._tooltipGroup.remove();
@ -363,13 +347,12 @@
// Apply formats optionally
if (!chart.noFormats) {
this.attr("opacity", function (d) { return (graded ? 1 : d.color.opacity); })
.attr("fill", function (d) { return (graded ? "url(#fill-line-gradient-" + d.keyString + ")" : d.color.fill); })
.attr("stroke", function (d) { return (graded ? "url(#stroke-line-gradient-" + d.keyString + ")" : d.color.stroke); })
.attr("fill", function (d) { return (graded ? "url(#fill-area-gradient-" + d.keyString + ")" : d.color.fill); })
.attr("stroke", function (d) { return (graded ? "url(#stroke-area-gradient-" + d.keyString + ")" : d.color.stroke); })
.attr("stroke-width", series.lineWeight);
}
})
.each(drawMarkers);
// Update
updated = dimple._handleTransition(theseShapes, duration)
.attr("d", function (d) { return d.update; })

View File

@ -20,6 +20,7 @@
interpolation,
graded = false,
i,
j,
k,
key,
keyString,
@ -27,7 +28,6 @@
updated,
removed,
orderedSeriesArray,
dataClone,
onEnter = function () {
return function (e, shape, chart, series) {
d3.select(shape).style("opacity", 1);
@ -52,14 +52,33 @@
} else {
val = dimple._helpers["c" + position](datum, chart, series);
}
return val;
// Remove long decimals from the coordinates as this fills the dom up with noise and makes matching below less likely to work. It
// shouldn't really matter but positioning to < 0.1 pixel is pretty pointless anyway.
return parseFloat(val.toFixed(1));
},
getLine = function (inter, originProperty) {
return d3.svg.line()
.x(function (d) { return (series.x._hasCategories() || !originProperty ? coord("x", d) : series.x[originProperty]); })
.y(function (d) { return (series.y._hasCategories() || !originProperty ? coord("y", d) : series.y[originProperty]); })
.x(function (d) { return (series.x._hasCategories() || !originProperty ? d.x : series.x[originProperty]); })
.y(function (d) { return (series.y._hasCategories() || !originProperty ? d.y : series.y[originProperty]); })
.interpolate(inter);
};
// coord = function (position, datum) {
// var val;
// if (series.interpolation === "step" && series[position]._hasCategories()) {
// series.barGap = 0;
// series.clusterBarGap = 0;
// val = dimple._helpers[position](datum, chart, series) + (position === "y" ? dimple._helpers.height(datum, chart, series) : 0);
// } else {
// val = dimple._helpers["c" + position](datum, chart, series);
// }
// return parseFloat(val).toFixed(1);
// },
// getLine = function (inter, originProperty) {
// return d3.svg.line()
// .x(function (d) { return (series.x._hasCategories() || !originProperty ? coord("x", d) : series.x[originProperty]); })
// .y(function (d) { return (series.y._hasCategories() || !originProperty ? coord("y", d) : series.y[originProperty]); })
// .interpolate(inter);
// };
// Handle the special interpolation handling for step
interpolation = (series.interpolation === "step" ? "step-after" : series.interpolation);
@ -95,8 +114,10 @@
keyString: keyString,
color: "white",
data: [],
points: [],
line: {},
entryExit: {}
entry: {},
exit: {}
});
}
// Add this row to the relevant data
@ -119,28 +140,55 @@
if (graded) {
dimple._addGradient(lineData[i].key, "fill-line-gradient-" + lineData[i].keyString, (series.x._hasCategories() ? series.x : series.y), data, chart, duration, "fill");
}
// Clone the data before adding elements
dataClone = [].concat(lineData[i].data);
// If this is a custom dimple step line duplicate the last datum so that the final step is completed
if (series.interpolation === "step") {
// Get points here, this is so that as well as drawing the line with them, we can also
// use them for the baseline
for (j = 0; j < lineData[i].data.length; j += 1) {
lineData[i].points.push({
x: coord("x", lineData[i].data[j]),
y: coord("y", lineData[i].data[j])
});
}
// If this is a step interpolation we need to add in some extra points to the category axis
// This is a little tricky but we need to add a new point duplicating the last category value. In order
// to place the point we need to calculate the gap between the last x and the penultimate x and apply that
// gap again.
if (series.interpolation === "step" && lineData[i].points.length > 1) {
if (series.x._hasCategories()) {
// Clone the last row and duplicate it.
dataClone = dataClone.concat(JSON.parse(JSON.stringify(dataClone[dataClone.length - 1])));
dataClone[dataClone.length - 1].cx = "";
dataClone[dataClone.length - 1].x = "";
}
if (series.y._hasCategories()) {
// Clone the last row and duplicate it.
dataClone = [JSON.parse(JSON.stringify(dataClone[0]))].concat(dataClone);
dataClone[0].cy = "";
dataClone[0].y = "";
lineData[i].points.push({
x : 2 * lineData[i].points[lineData[i].points.length - 1].x - lineData[i].points[lineData[i].points.length - 2].x,
y : lineData[i].points[lineData[i].points.length - 1].y
});
} else if (series.y._hasCategories()) {
lineData[i].points = [{
x : lineData[i].points[0].x,
y : 2 * lineData[i].points[0].y - lineData[i].points[1].y
}].concat(lineData[i].points);
}
}
// // Clone the data before adding elements
// dataClone = [].concat(lineData[i].data);
// // If this is a custom dimple step line duplicate the last datum so that the final step is completed
// if (series.interpolation === "step") {
// if (series.x._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = dataClone.concat(JSON.parse(JSON.stringify(dataClone[dataClone.length - 1])));
// dataClone[dataClone.length - 1].cx = "";
// dataClone[dataClone.length - 1].x = "";
// }
// if (series.y._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = [JSON.parse(JSON.stringify(dataClone[0]))].concat(dataClone);
// dataClone[0].cy = "";
// dataClone[0].y = "";
// }
// }
// Get the points that this line will appear
lineData[i].entry = getLine(interpolation, "_previousOrigin")(dataClone);
lineData[i].update = getLine(interpolation)(dataClone);
lineData[i].exit = getLine(interpolation, "_origin")(dataClone);
lineData[i].entry = getLine(interpolation, "_previousOrigin")(lineData[i].points);
lineData[i].update = getLine(interpolation)(lineData[i].points);
lineData[i].exit = getLine(interpolation, "_origin")(lineData[i].points);
// Add the color in this loop, it can't be done during initialisation of the row because
// the lines should be ordered first (to ensure standard distribution of colors

View File

@ -2643,6 +2643,11 @@ var dimple = {
basePoints,
basePoint,
cat,
catVal,
group,
p,
b,
l,
lastAngle,
catCoord,
valCoord,
@ -2680,6 +2685,9 @@ var dimple = {
.y(function (d) { return (series.y._hasCategories() || !originProperty ? d.y : series.y[originProperty]); })
.interpolate(inter);
},
sortByVal = function (a, b) {
return parseFloat(a) - parseFloat(b);
},
sortByX = function (a, b) {
return parseFloat(a.x) - parseFloat(b.x);
},
@ -2750,7 +2758,9 @@ var dimple = {
points: [],
area: {},
entry: {},
exit: {}
exit: {},
// Group refers to groupings along the category axis. If there are groupings it will be recorded, otherwise all is used as a default
group: (catCoord && data[i][catCoord + "Field"] && data[i][catCoord + "Field"].length >= 2 ? data[i][catCoord + "Field"][0] : "All")
});
}
// Add this row to the relevant data
@ -2779,7 +2789,32 @@ var dimple = {
// if there is a category axis, add the points to a distinct set. Set these to use the origin value
// this will be updated with the last value in each case as we build the areas
if (catCoord) {
catPoints[areaData[i].points[areaData[i].points.length - 1][catCoord]] = series[valCoord]._previousOrigin;
if (!catPoints[areaData[i].group]) {
catPoints[areaData[i].group] = {};
}
catPoints[areaData[i].group][areaData[i].points[areaData[i].points.length - 1][catCoord]] = series[valCoord]._previousOrigin;
}
}
points = areaData[i].points;
// If this is a step interpolation we need to add in some extra points to the category axis
// This is a little tricky but we need to add a new point duplicating the last category value. In order
// to place the point we need to calculate the gap between the last x and the penultimate x and apply that
// gap again.
if (series.interpolation === "step" && points.length > 1 && catCoord) {
if (series.x._hasCategories()) {
points.push({
x : 2 * points[points.length - 1].x - points[points.length - 2].x,
y : points[points.length - 1].y
});
catPoints[areaData[i].group][points[points.length - 1][catCoord]] = series[valCoord]._previousOrigin;
} else if (series.y._hasCategories()) {
points = [{
x : points[0].x,
y : 2 * points[0].y - points[1].y
}].concat(points);
catPoints[areaData[i].group][points[0][catCoord]] = series[valCoord]._previousOrigin;
// The prepend above breaks the reference so it needs to be reapplied here.
areaData[i].points = points;
}
}
}
@ -2787,47 +2822,54 @@ var dimple = {
// catPoints needs to be lookup, but also accessed sequentially so we need to create an array of keys
for (cat in catPoints) {
if (catPoints.hasOwnProperty(cat)) {
allPoints.push(parseFloat(cat));
allPoints[cat] = [];
for (catVal in catPoints[cat]) {
if (catPoints[cat].hasOwnProperty(catVal)) {
allPoints[cat].push(parseFloat(catVal));
}
}
// Sort the points as integers
allPoints[cat].sort(sortByVal);
}
}
// Sort the points as integers
allPoints.sort(function (a, b) { return parseFloat(a) - parseFloat(b); });
// Create the areas
for (i = 0; i < areaData.length; i += 1) {
points = areaData[i].points;
group = areaData[i].group;
basePoints = [];
finalPointArray = [];
// If this should have colour gradients, add them
if (graded) {
dimple._addGradient(areaData[i].key, "fill-area-gradient-" + areaData[i].keyString, (series.x._hasCategories() ? series.x : series.y), data, chart, duration, "fill");
}
// All points will only be populated if there is a category axis
if (allPoints && allPoints.length > 0) {
if (allPoints[group] && allPoints[group].length > 0) {
// Iterate the point array because we need to fill in zero points for missing ones, otherwise the areas
// will cross where an upper area has no value and a lower value has a spike Issue #7
for (j = 0, k = 0; j < allPoints.length; j += 1) {
for (j = 0, k = 0; j < allPoints[group].length; j += 1) {
// We are only interested in points between the first and last point of this areas data (i.e. don't fill ends - important
// for grouped area charts)
if (allPoints[j] >= points[0][catCoord] && allPoints[j] <= points[points.length - 1][catCoord]) {
// for grouped area charts). We have to use a strange criteria here. If there are no group gaps on a grouped area
// chart the end point of one series will clash with the start point of another, therefore we have to ignore fill-in's within
// a couple of pixels of the start and end points
if (allPoints[group][j] >= points[0][catCoord] && allPoints[group][j] <= points[points.length - 1][catCoord]) {
// Get a base point, this needs to go on the base points array as well as filling in gaps in the point array.
// Create a point using the coordinate on the category axis and the last recorded value
// position from the dictionary
basePoint = {};
basePoint[catCoord] = allPoints[j];
basePoint[valCoord] = catPoints[allPoints[j]];
basePoint[catCoord] = allPoints[group][j];
basePoint[valCoord] = catPoints[group][allPoints[group][j]];
// add the base point
basePoints.push(basePoint);
// handle missing points
if (points[k][catCoord] > allPoints[j]) {
if (points[k][catCoord] > allPoints[group][j]) {
// If there is a missing point we need to in fill
finalPointArray.push(basePoint);
} else {
// They must be the same
finalPointArray.push(points[k]);
// Use this to update the dictionary to the new value coordinate
catPoints[allPoints[j]] = points[k][valCoord];
catPoints[areaData[i].group][allPoints[group][j]] = points[k][valCoord];
k += 1;
}
}
@ -2863,88 +2905,30 @@ var dimple = {
}
}
// The final array of points for the entire outskirts of the area
finalPointArray = finalPointArray.concat(basePoints.reverse()).concat(finalPointArray[0]);
// Reverse the base points so that they are in the correct order for the path
basePoints = basePoints.reverse();
// Get the points that this area will appear
areaData[i].entry = getArea(interpolation, "_previousOrigin")(finalPointArray);
areaData[i].update = getArea(interpolation)(finalPointArray);
areaData[i].exit = getArea(interpolation, "_origin")(finalPointArray);
p = getArea(interpolation, "_previousOrigin")(finalPointArray);
b = getArea((interpolation === "step-after" ? "step-before" : (interpolation === "step-before" ? "step-after" : interpolation)), "_previousOrigin")(basePoints);
l = getArea("linear", "_previousOrigin")(finalPointArray);
areaData[i].entry = p + "L" + b.substring(1) + "L" + l.substring(1, l.indexOf("L"));
p = getArea(interpolation)(finalPointArray);
b = getArea(interpolation === "step-after" ? "step-before" : (interpolation === "step-before" ? "step-after" : interpolation))(basePoints);
l = getArea("linear")(finalPointArray);
areaData[i].update = p + "L" + b.substring(1) + "L" + l.substring(1, l.indexOf("L"));
p = getArea(interpolation, "_origin")(finalPointArray);
b = getArea((interpolation === "step-after" ? "step-before" : (interpolation === "step-before" ? "step-after" : interpolation)), "_origin")(basePoints);
l = getArea("linear", "_origin")(finalPointArray);
areaData[i].exit = p + "L" + b.substring(1) + "L" + l.substring(1, l.indexOf("L"));
// Add the color in this loop, it can't be done during initialisation of the row because
// the areas should be ordered first (to ensure standard distribution of colors
areaData[i].color = chart.getColor(areaData[i].key.length > 0 ? areaData[i].key[areaData[i].key.length - 1] : "All");
}
// // Create a set of area data grouped by the aggregation field
// for (i = 0; i < areaData.length; i += 1) {
//
// // Sort the points so that areas are connected in the correct order
// areaData[i].data.sort(dimple._getSeriesSortPredicate(chart, series, orderedSeriesArray));
//
// // If this should have colour gradients, add them
// if (graded) {
// dimple._addGradient(areaData[i].key, "fill-area-gradient-" + areaData[i].keyString, (series.x._hasCategories() ? series.x : series.y), data, chart, duration, "fill");
// }
//
// // Clone the data before adding elements
// dataClone = [].concat(areaData[i].data);
//
// // If this is a custom dimple step area duplicate the last datum so that the final step is completed
// if (series.interpolation === "step") {
// if (series.x._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = dataClone.concat(JSON.parse(JSON.stringify(dataClone[dataClone.length - 1])));
// dataClone[dataClone.length - 1].cx = "";
// dataClone[dataClone.length - 1].x = "";
// }
// if (series.y._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = [JSON.parse(JSON.stringify(dataClone[0]))].concat(dataClone);
// dataClone[0].cy = "";
// dataClone[0].y = "";
// }
// }
//
// // Add zero baseline for this row
// if (series.x._hasCategories() && series.y._hasMeasure()) {
// for (j = dataClone.length - 1; j >= 0; j -= 1) {
// areaData[i].baseData.push({
// x : dataClone[j].x,
// cx : dataClone[j].cx,
// xOffset : dataClone[j].xOffset,
// width : dataClone[j].width,
// y: (i === 0 ? 0 : areaData[i - 1].data[j].y),
// cy : (i === 0 ? 0 : areaData[i - 1].data[j].cy),
// yOffset: (i === 0 ? 0 : areaData[i - 1].data[j].yOffset),
// height: (i === 0 ? 0 : areaData[i - 1].data[j].height)
// });
// }
// } else if (series.y._hasCategories() && series.x._hasMeasure()) {
// for (j = dataClone.length - 1; j >= 0; j -= 1) {
// areaData[i].baseData.push({
// x : (i === 0 ? 0 : areaData[i - 1].data[j].x),
// cx : (i === 0 ? 0 : areaData[i - 1].data[j].cx),
// xOffset : (i === 0 ? 0 : areaData[i - 1].data[j].xOffset),
// width : (i === 0 ? 0 : areaData[i - 1].data[j].width),
// y: dataClone[j].y,
// cy : dataClone[j].cy,
// yOffset: dataClone[j].yOffset,
// height: dataClone[j].height
// });
// }
// }
//
// // Get the points that this area will appear
// areaData[i].entry = getArea(interpolation, "_previousOrigin")(areaData[i].baseData.concat(dataClone));
// areaData[i].update = getArea(interpolation)(areaData[i].baseData.concat(dataClone));
// areaData[i].exit = getArea(interpolation, "_origin")(areaData[i].baseData.concat(dataClone));
//
// // Add the color in this loop, it can't be done during initialisation of the row because
// // the areas should be ordered first (to ensure standard distribution of colors
// areaData[i].color = chart.getColor(areaData[i].key.length > 0 ? areaData[i].key[areaData[i].key.length - 1] : "All");
//
// }
if (chart._tooltipGroup !== null && chart._tooltipGroup !== undefined) {
chart._tooltipGroup.remove();
@ -2971,13 +2955,12 @@ var dimple = {
// Apply formats optionally
if (!chart.noFormats) {
this.attr("opacity", function (d) { return (graded ? 1 : d.color.opacity); })
.attr("fill", function (d) { return (graded ? "url(#fill-line-gradient-" + d.keyString + ")" : d.color.fill); })
.attr("stroke", function (d) { return (graded ? "url(#stroke-line-gradient-" + d.keyString + ")" : d.color.stroke); })
.attr("fill", function (d) { return (graded ? "url(#fill-area-gradient-" + d.keyString + ")" : d.color.fill); })
.attr("stroke", function (d) { return (graded ? "url(#stroke-area-gradient-" + d.keyString + ")" : d.color.stroke); })
.attr("stroke-width", series.lineWeight);
}
})
.each(drawMarkers);
// Update
updated = dimple._handleTransition(theseShapes, duration)
.attr("d", function (d) { return d.update; })
@ -3222,6 +3205,7 @@ var dimple = {
interpolation,
graded = false,
i,
j,
k,
key,
keyString,
@ -3229,7 +3213,6 @@ var dimple = {
updated,
removed,
orderedSeriesArray,
dataClone,
onEnter = function () {
return function (e, shape, chart, series) {
d3.select(shape).style("opacity", 1);
@ -3254,14 +3237,33 @@ var dimple = {
} else {
val = dimple._helpers["c" + position](datum, chart, series);
}
return val;
// Remove long decimals from the coordinates as this fills the dom up with noise and makes matching below less likely to work. It
// shouldn't really matter but positioning to < 0.1 pixel is pretty pointless anyway.
return parseFloat(val.toFixed(1));
},
getLine = function (inter, originProperty) {
return d3.svg.line()
.x(function (d) { return (series.x._hasCategories() || !originProperty ? coord("x", d) : series.x[originProperty]); })
.y(function (d) { return (series.y._hasCategories() || !originProperty ? coord("y", d) : series.y[originProperty]); })
.x(function (d) { return (series.x._hasCategories() || !originProperty ? d.x : series.x[originProperty]); })
.y(function (d) { return (series.y._hasCategories() || !originProperty ? d.y : series.y[originProperty]); })
.interpolate(inter);
};
// coord = function (position, datum) {
// var val;
// if (series.interpolation === "step" && series[position]._hasCategories()) {
// series.barGap = 0;
// series.clusterBarGap = 0;
// val = dimple._helpers[position](datum, chart, series) + (position === "y" ? dimple._helpers.height(datum, chart, series) : 0);
// } else {
// val = dimple._helpers["c" + position](datum, chart, series);
// }
// return parseFloat(val).toFixed(1);
// },
// getLine = function (inter, originProperty) {
// return d3.svg.line()
// .x(function (d) { return (series.x._hasCategories() || !originProperty ? coord("x", d) : series.x[originProperty]); })
// .y(function (d) { return (series.y._hasCategories() || !originProperty ? coord("y", d) : series.y[originProperty]); })
// .interpolate(inter);
// };
// Handle the special interpolation handling for step
interpolation = (series.interpolation === "step" ? "step-after" : series.interpolation);
@ -3297,8 +3299,10 @@ var dimple = {
keyString: keyString,
color: "white",
data: [],
points: [],
line: {},
entryExit: {}
entry: {},
exit: {}
});
}
// Add this row to the relevant data
@ -3321,28 +3325,55 @@ var dimple = {
if (graded) {
dimple._addGradient(lineData[i].key, "fill-line-gradient-" + lineData[i].keyString, (series.x._hasCategories() ? series.x : series.y), data, chart, duration, "fill");
}
// Clone the data before adding elements
dataClone = [].concat(lineData[i].data);
// If this is a custom dimple step line duplicate the last datum so that the final step is completed
if (series.interpolation === "step") {
// Get points here, this is so that as well as drawing the line with them, we can also
// use them for the baseline
for (j = 0; j < lineData[i].data.length; j += 1) {
lineData[i].points.push({
x: coord("x", lineData[i].data[j]),
y: coord("y", lineData[i].data[j])
});
}
// If this is a step interpolation we need to add in some extra points to the category axis
// This is a little tricky but we need to add a new point duplicating the last category value. In order
// to place the point we need to calculate the gap between the last x and the penultimate x and apply that
// gap again.
if (series.interpolation === "step" && lineData[i].points.length > 1) {
if (series.x._hasCategories()) {
// Clone the last row and duplicate it.
dataClone = dataClone.concat(JSON.parse(JSON.stringify(dataClone[dataClone.length - 1])));
dataClone[dataClone.length - 1].cx = "";
dataClone[dataClone.length - 1].x = "";
}
if (series.y._hasCategories()) {
// Clone the last row and duplicate it.
dataClone = [JSON.parse(JSON.stringify(dataClone[0]))].concat(dataClone);
dataClone[0].cy = "";
dataClone[0].y = "";
lineData[i].points.push({
x : 2 * lineData[i].points[lineData[i].points.length - 1].x - lineData[i].points[lineData[i].points.length - 2].x,
y : lineData[i].points[lineData[i].points.length - 1].y
});
} else if (series.y._hasCategories()) {
lineData[i].points = [{
x : lineData[i].points[0].x,
y : 2 * lineData[i].points[0].y - lineData[i].points[1].y
}].concat(lineData[i].points);
}
}
// // Clone the data before adding elements
// dataClone = [].concat(lineData[i].data);
// // If this is a custom dimple step line duplicate the last datum so that the final step is completed
// if (series.interpolation === "step") {
// if (series.x._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = dataClone.concat(JSON.parse(JSON.stringify(dataClone[dataClone.length - 1])));
// dataClone[dataClone.length - 1].cx = "";
// dataClone[dataClone.length - 1].x = "";
// }
// if (series.y._hasCategories()) {
// // Clone the last row and duplicate it.
// dataClone = [JSON.parse(JSON.stringify(dataClone[0]))].concat(dataClone);
// dataClone[0].cy = "";
// dataClone[0].y = "";
// }
// }
// Get the points that this line will appear
lineData[i].entry = getLine(interpolation, "_previousOrigin")(dataClone);
lineData[i].update = getLine(interpolation)(dataClone);
lineData[i].exit = getLine(interpolation, "_origin")(dataClone);
lineData[i].entry = getLine(interpolation, "_previousOrigin")(lineData[i].points);
lineData[i].update = getLine(interpolation)(lineData[i].points);
lineData[i].exit = getLine(interpolation, "_origin")(lineData[i].points);
// Add the color in this loop, it can't be done during initialisation of the row because
// the lines should be ordered first (to ensure standard distribution of colors