Skip to content

Commit b95e462

Browse files
committed
add and 🔒 typed array support for extendTraces and prependTraces
... by merging the concat and splice steps (which can't be done using native methods on typed arrays)
1 parent a7ed2c2 commit b95e462

File tree

2 files changed

+284
-78
lines changed

2 files changed

+284
-78
lines changed

src/plot_api/plot_api.js

Lines changed: 116 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -890,12 +890,15 @@ function getExtendProperties(gd, update, indices, maxPoints) {
890890
target = prop.get();
891891
insert = update[key][j];
892892

893-
if(!Array.isArray(insert)) {
893+
if(!Lib.isArrayOrTypedArray(insert)) {
894894
throw new Error('attribute: ' + key + ' index: ' + j + ' must be an array');
895895
}
896-
if(!Array.isArray(target)) {
896+
if(!Lib.isArrayOrTypedArray(target)) {
897897
throw new Error('cannot extend missing or non-array attribute: ' + key);
898898
}
899+
if(target.constructor !== insert.constructor) {
900+
throw new Error('cannot extend array with an array of a different type: ' + key);
901+
}
899902

900903
/*
901904
* maxPoints may be an object map or a scalar. If object select the key:value, else
@@ -931,64 +934,43 @@ function getExtendProperties(gd, update, indices, maxPoints) {
931934
* @param {Object} update
932935
* @param {Number[]} indices
933936
* @param {Number||Object} maxPoints
934-
* @param {Function} lengthenArray
935-
* @param {Function} spliceArray
937+
* @param {Function} updateArray
936938
* @return {Object}
937939
*/
938-
function spliceTraces(gd, update, indices, maxPoints, lengthenArray, spliceArray) {
939-
940+
function spliceTraces(gd, update, indices, maxPoints, updateArray) {
940941
assertExtendTracesArgs(gd, update, indices, maxPoints);
941942

942-
var updateProps = getExtendProperties(gd, update, indices, maxPoints),
943-
remainder = [],
944-
undoUpdate = {},
945-
undoPoints = {};
946-
var target, prop, maxp;
943+
var updateProps = getExtendProperties(gd, update, indices, maxPoints);
944+
var undoUpdate = {};
945+
var undoPoints = {};
947946

948947
for(var i = 0; i < updateProps.length; i++) {
948+
var prop = updateProps[i].prop;
949+
var maxp = updateProps[i].maxp;
949950

950-
/*
951-
* prop is the object returned by Lib.nestedProperties
952-
*/
953-
prop = updateProps[i].prop;
954-
maxp = updateProps[i].maxp;
955-
956-
target = lengthenArray(updateProps[i].target, updateProps[i].insert);
957-
958-
/*
959-
* If maxp is set within post-extension trace.length, splice to maxp length.
960-
* Otherwise skip function call as splice op will have no effect anyway.
961-
*/
962-
if(maxp >= 0 && maxp < target.length) remainder = spliceArray(target, maxp);
963-
964-
/*
965-
* to reverse this operation we need the size of the original trace as the reverse
966-
* operation will need to window out any lengthening operation performed in this pass.
967-
*/
968-
maxp = updateProps[i].target.length;
969-
970-
/*
971-
* Magic happens here! update gd.data.trace[key] with new array data.
972-
*/
973-
prop.set(target);
951+
// return new array and remainder
952+
var out = updateArray(updateProps[i].target, updateProps[i].insert, maxp);
953+
prop.set(out[0]);
974954

955+
// build the inverse update object for the undo operation
975956
if(!Array.isArray(undoUpdate[prop.astr])) undoUpdate[prop.astr] = [];
976-
if(!Array.isArray(undoPoints[prop.astr])) undoPoints[prop.astr] = [];
977-
978-
/*
979-
* build the inverse update object for the undo operation
980-
*/
981-
undoUpdate[prop.astr].push(remainder);
957+
undoUpdate[prop.astr].push(out[1]);
982958

983-
/*
984-
* build the matching maxPoints undo object containing original trace lengths.
985-
*/
986-
undoPoints[prop.astr].push(maxp);
959+
// build the matching maxPoints undo object containing original trace lengths
960+
if(!Array.isArray(undoPoints[prop.astr])) undoPoints[prop.astr] = [];
961+
undoPoints[prop.astr].push(updateProps[i].target.length);
987962
}
988963

989964
return {update: undoUpdate, maxPoints: undoPoints};
990965
}
991966

967+
function concatTypedArray(arr0, arr1) {
968+
var arr2 = new arr0.constructor(arr0.length + arr1.length);
969+
arr2.set(arr0);
970+
arr2.set(arr1, arr0.length);
971+
return arr2;
972+
}
973+
992974
/**
993975
* extend && prepend traces at indices with update arrays, window trace lengths to maxPoints
994976
*
@@ -1009,24 +991,55 @@ function spliceTraces(gd, update, indices, maxPoints, lengthenArray, spliceArray
1009991
Plotly.extendTraces = function extendTraces(gd, update, indices, maxPoints) {
1010992
gd = Lib.getGraphDiv(gd);
1011993

1012-
var undo = spliceTraces(gd, update, indices, maxPoints,
994+
function updateArray(target, insert, maxp) {
995+
var newArray, remainder;
1013996

1014-
/*
1015-
* The Lengthen operation extends trace from end with insert
1016-
*/
1017-
function(target, insert) {
1018-
return target.concat(insert);
1019-
},
997+
if(Lib.isTypedArray(target)) {
998+
if(maxp < 0) {
999+
var none = new target.constructor(0);
1000+
var both = concatTypedArray(target, insert);
10201001

1021-
/*
1022-
* Window the trace keeping maxPoints, counting back from the end
1023-
*/
1024-
function(target, maxPoints) {
1025-
return target.splice(0, target.length - maxPoints);
1026-
});
1002+
if(maxp < 0) {
1003+
newArray = both;
1004+
remainder = none;
1005+
} else {
1006+
newArray = none;
1007+
remainder = both;
1008+
}
1009+
} else {
1010+
newArray = new target.constructor(maxp);
1011+
remainder = new target.constructor(target.length + insert.length - maxp);
1012+
1013+
if(maxp === insert.length) {
1014+
newArray.set(insert);
1015+
remainder.set(target);
1016+
} else if(maxp < insert.length) {
1017+
var numberOfItemsFromInsert = insert.length - maxp;
1018+
1019+
newArray.set(insert.subarray(numberOfItemsFromInsert));
1020+
remainder.set(target);
1021+
remainder.set(insert.subarray(0, numberOfItemsFromInsert), target.length);
1022+
} else {
1023+
var numberOfItemsFromTarget = maxp - insert.length;
1024+
var targetBegin = target.length - numberOfItemsFromTarget;
10271025

1028-
var promise = Plotly.redraw(gd);
1026+
newArray.set(target.subarray(targetBegin));
1027+
newArray.set(insert, numberOfItemsFromTarget);
1028+
remainder.set(target.subarray(0, targetBegin));
1029+
}
1030+
}
1031+
} else {
1032+
newArray = target.concat(insert);
1033+
remainder = (maxp >= 0 && maxp < newArray.length) ?
1034+
newArray.splice(0, newArray.length - maxp) :
1035+
[];
1036+
}
1037+
1038+
return [newArray, remainder];
1039+
}
10291040

1041+
var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
1042+
var promise = Plotly.redraw(gd);
10301043
var undoArgs = [gd, undo.update, indices, undo.maxPoints];
10311044
Queue.add(gd, Plotly.prependTraces, undoArgs, extendTraces, arguments);
10321045

@@ -1036,24 +1049,54 @@ Plotly.extendTraces = function extendTraces(gd, update, indices, maxPoints) {
10361049
Plotly.prependTraces = function prependTraces(gd, update, indices, maxPoints) {
10371050
gd = Lib.getGraphDiv(gd);
10381051

1039-
var undo = spliceTraces(gd, update, indices, maxPoints,
1052+
function updateArray(target, insert, maxp) {
1053+
var newArray, remainder;
10401054

1041-
/*
1042-
* The Lengthen operation extends trace by appending insert to start
1043-
*/
1044-
function(target, insert) {
1045-
return insert.concat(target);
1046-
},
1055+
if(Lib.isTypedArray(target)) {
1056+
if(maxp <= 0) {
1057+
var none = new target.constructor(0);
1058+
var both = concatTypedArray(insert, target);
10471059

1048-
/*
1049-
* Window the trace keeping maxPoints, counting forward from the start
1050-
*/
1051-
function(target, maxPoints) {
1052-
return target.splice(maxPoints, target.length);
1053-
});
1060+
if(maxp < 0) {
1061+
newArray = both;
1062+
remainder = none;
1063+
} else {
1064+
newArray = none;
1065+
remainder = both;
1066+
}
1067+
} else {
1068+
newArray = new target.constructor(maxp);
1069+
remainder = new target.constructor(target.length + insert.length - maxp);
1070+
1071+
if(maxp === insert.length) {
1072+
newArray.set(insert);
1073+
remainder.set(target);
1074+
} else if(maxp < insert.length) {
1075+
var numberOfItemsFromInsert = insert.length - maxp;
1076+
1077+
newArray.set(insert.subarray(0, numberOfItemsFromInsert));
1078+
remainder.set(insert.subarray(numberOfItemsFromInsert));
1079+
remainder.set(target, numberOfItemsFromInsert);
1080+
} else {
1081+
var numberOfItemsFromTarget = maxp - insert.length;
10541082

1055-
var promise = Plotly.redraw(gd);
1083+
newArray.set(insert);
1084+
newArray.set(target.subarray(0, numberOfItemsFromTarget), insert.length);
1085+
remainder.set(target.subarray(numberOfItemsFromTarget));
1086+
}
1087+
}
1088+
} else {
1089+
newArray = insert.concat(target);
1090+
remainder = (maxp >= 0 && maxp < newArray.length) ?
1091+
newArray.splice(maxp, newArray.length) :
1092+
[];
1093+
}
10561094

1095+
return [newArray, remainder];
1096+
}
1097+
1098+
var undo = spliceTraces(gd, update, indices, maxPoints, updateArray);
1099+
var promise = Plotly.redraw(gd);
10571100
var undoArgs = [gd, undo.update, indices, undo.maxPoints];
10581101
Queue.add(gd, Plotly.extendTraces, undoArgs, prependTraces, arguments);
10591102

0 commit comments

Comments
 (0)