Skip to content

Commit f01db07

Browse files
committed
generalize info_array to 2D and arb. length
1 parent 606a601 commit f01db07

File tree

3 files changed

+145
-11
lines changed

3 files changed

+145
-11
lines changed

src/lib/coerce.js

+55-8
Original file line numberDiff line numberDiff line change
@@ -257,19 +257,56 @@ exports.valObjectMeta = {
257257
'An {array} of plot information.'
258258
].join(' '),
259259
requiredOpts: ['items'],
260-
otherOpts: ['dflt', 'freeLength'],
260+
// set dimensions=2 for a 2D array
261+
// `items` may be a single object instead of an array, in which case
262+
// `freeLength` must be true.
263+
otherOpts: ['dflt', 'freeLength', 'dimensions'],
261264
coerceFunction: function(v, propOut, dflt, opts) {
265+
266+
// simplified coerce function just for array items
267+
function coercePart(v, opts, dflt) {
268+
var out;
269+
var propPart = {set: function(v) { out = v; }};
270+
271+
if(dflt === undefined) dflt = opts.dflt;
272+
273+
exports.valObjectMeta[opts.valType].coerceFunction(v, propPart, dflt, opts);
274+
275+
return out;
276+
}
277+
278+
var twoD = opts.dimensions === 2;
279+
262280
if(!Array.isArray(v)) {
263281
propOut.set(dflt);
264282
return;
265283
}
266284

267-
var items = opts.items,
268-
vOut = [];
285+
var items = opts.items;
286+
var vOut = [];
287+
var arrayItems = Array.isArray(items);
288+
var len = arrayItems ? items.length : v.length;
289+
290+
var i, j, len2, vNew;
291+
269292
dflt = Array.isArray(dflt) ? dflt : [];
270293

271-
for(var i = 0; i < items.length; i++) {
272-
exports.coerce(v, vOut, items, '[' + i + ']', dflt[i]);
294+
if(twoD) {
295+
for(i = 0; i < len; i++) {
296+
vOut[i] = [];
297+
var row = Array.isArray(v[i]) ? v[i] : [];
298+
len2 = arrayItems ? items[i].length : row.length;
299+
for(j = 0; j < len2; j++) {
300+
vNew = coercePart(row[j], arrayItems ? items[i][j] : items, (dflt[i] || [])[j]);
301+
if(vNew !== undefined) vOut[i][j] = vNew;
302+
}
303+
}
304+
}
305+
else {
306+
for(i = 0; i < len; i++) {
307+
vNew = coercePart(v[i], arrayItems ? items[i] : items, dflt[i]);
308+
if(vNew !== undefined) vOut[i] = vNew;
309+
}
273310
}
274311

275312
propOut.set(vOut);
@@ -278,15 +315,25 @@ exports.valObjectMeta = {
278315
if(!Array.isArray(v)) return false;
279316

280317
var items = opts.items;
318+
var arrayItems = Array.isArray(items);
319+
var twoD = opts.dimensions === 2;
281320

282321
// when free length is off, input and declared lengths must match
283322
if(!opts.freeLength && v.length !== items.length) return false;
284323

285324
// valid when all input items are valid
286325
for(var i = 0; i < v.length; i++) {
287-
var isItemValid = exports.validate(v[i], opts.items[i]);
288-
289-
if(!isItemValid) return false;
326+
if(twoD) {
327+
if(!Array.isArray(v[i]) || (!opts.freeLength && v[i].length !== items[i].length)) {
328+
return false;
329+
}
330+
for(var j = 0; j < v[i].length; j++) {
331+
if(!exports.validate(v[i][j], arrayItems ? items[i][j] : items)) {
332+
return false;
333+
}
334+
}
335+
}
336+
else if(!exports.validate(v[i], arrayItems ? items[i] : items)) return false;
290337
}
291338

292339
return true;

src/plot_api/plot_schema.js

+19-3
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,8 @@ function recurseIntoValObject(valObject, parts, i) {
383383
}
384384

385385
// now recurse as far as we can. Occasionally we have an attribute
386-
// setting an internal part below what's
386+
// setting an internal part below what's in the schema; just return
387+
// the innermost schema item we find.
387388
for(; i < parts.length; i++) {
388389
var newValObject = valObject[parts[i]];
389390
if(Lib.isPlainObject(newValObject)) valObject = newValObject;
@@ -398,8 +399,23 @@ function recurseIntoValObject(valObject, parts, i) {
398399
else if(valObject.valType === 'info_array') {
399400
i++;
400401
var index = parts[i];
401-
if(!isIndex(index) || index >= valObject.items.length) return false;
402-
valObject = valObject.items[index];
402+
if(!isIndex(index)) return false;
403+
404+
var items = valObject.items;
405+
if(Array.isArray(items)) {
406+
if(index >= items.length) return false;
407+
if(valObject.dimensions === 2) {
408+
i++;
409+
if(parts.length === i) return valObject;
410+
var index2 = parts[i];
411+
if(!isIndex(index2)) return false;
412+
valObject = items[index][index2];
413+
}
414+
else valObject = items[index];
415+
}
416+
else {
417+
valObject = items;
418+
}
403419
}
404420
}
405421

test/jasmine/tests/lib_test.js

+71
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,77 @@ describe('Test lib.js:', function() {
797797
expect(coerce({domain: [0, 0.5, 1]}, {}, infoArrayAttrs, 'domain'))
798798
.toEqual([0, 0.5]);
799799
});
800+
801+
it('supports bounded freeLength attributes', function() {
802+
var attrs = {
803+
x: {
804+
valType: 'info_array',
805+
freeLength: true,
806+
items: [
807+
{valType: 'integer', min: 0},
808+
{valType: 'integer', max: -1}
809+
],
810+
dflt: [1, -2]
811+
},
812+
};
813+
expect(coerce({}, {}, attrs, 'x')).toEqual([1, -2]);
814+
expect(coerce({x: []}, {}, attrs, 'x')).toEqual([1, -2]);
815+
expect(coerce({x: [5]}, {}, attrs, 'x')).toEqual([5, -2]);
816+
expect(coerce({x: [-5]}, {}, attrs, 'x')).toEqual([1, -2]);
817+
expect(coerce({x: [5, -5]}, {}, attrs, 'x')).toEqual([5, -5]);
818+
expect(coerce({x: [3, -3, 3]}, {}, attrs, 'x')).toEqual([3, -3]);
819+
});
820+
821+
it('supports unbounded freeLength attributes', function() {
822+
var attrs = {
823+
x: {
824+
valType: 'info_array',
825+
freeLength: true,
826+
items: {valType: 'integer', min: 0, dflt: 1}
827+
}
828+
};
829+
expect(coerce({}, {}, attrs, 'x')).toBeUndefined();
830+
expect(coerce({x: []}, {}, attrs, 'x')).toEqual([]);
831+
expect(coerce({x: [3]}, {}, attrs, 'x')).toEqual([3]);
832+
expect(coerce({x: [-3]}, {}, attrs, 'x')).toEqual([1]);
833+
expect(coerce({x: [-1, 4, 'hi', 5]}, {}, attrs, 'x'))
834+
.toEqual([1, 4, 1, 5]);
835+
});
836+
837+
it('supports 2D fixed-size arrays', function() {
838+
var attrs = {
839+
x: {
840+
valType: 'info_array',
841+
dimensions: 2,
842+
items: [
843+
[{valType: 'integer', min: 0, max: 2}, {valType: 'integer', min: 3, max: 5}],
844+
[{valType: 'integer', min: 6, max: 8}, {valType: 'integer', min: 9, max: 11}]
845+
],
846+
dflt: [[1, 4], [7, 10]]
847+
}
848+
};
849+
expect(coerce({}, {}, attrs, 'x')).toEqual([[1, 4], [7, 10]]);
850+
expect(coerce({x: []}, {}, attrs, 'x')).toEqual([[1, 4], [7, 10]]);
851+
expect(coerce({x: [[0, 3], [8, 11]]}, {}, attrs, 'x'))
852+
.toEqual([[0, 3], [8, 11]]);
853+
expect(coerce({x: [[10, 5, 10], [6], [1, 2, 3]]}, {}, attrs, 'x'))
854+
.toEqual([[1, 5], [6, 10]]);
855+
});
856+
857+
it('supports unbounded 2D freeLength arrays', function() {
858+
var attrs = {
859+
x: {
860+
valType: 'info_array',
861+
freeLength: true,
862+
dimensions: 2,
863+
items: {valType: 'integer', min: 0, dflt: 1}
864+
}
865+
};
866+
expect(coerce({}, {}, attrs, 'x')).toBeUndefined();
867+
expect(coerce({x: []}, {}, attrs, 'x')).toEqual([]);
868+
expect(coerce({x: [[], [0], [-1, 2], [5, 'a', 4, 6.6]]}, {}, attrs, 'x'))
869+
.toEqual([[], [0], [1, 2], [5, 1, 4, 1]]);
870+
});
800871
});
801872

802873
describe('subplotid valtype', function() {

0 commit comments

Comments
 (0)