Skip to content

Commit 884afdf

Browse files
committed
misc improvements after AJ's feedback
- dive into 2d arrays to estimate their lengths - bump box, violin, histogram and histogram2d max pt threshold to 1e6, and add special case for box and violin with sample points displayed - use budget so that multiple traces close to threshold won't hang exporter - make traces w/o array don't mess up count
1 parent 2916aee commit 884afdf

File tree

2 files changed

+128
-13
lines changed

2 files changed

+128
-13
lines changed

src/component/plotly-graph/parse.js

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -120,30 +120,42 @@ function willFigureHang (result) {
120120
return true
121121
}
122122

123-
const numberOfPtsPerTraceType = {}
123+
let maxPtBudget = 0
124124

125125
for (let i = 0; i < data.length; i++) {
126126
const trace = data[i] || {}
127127
const type = trace.type || 'scatter'
128+
const len = estimateDataLength(trace)
128129

129-
if (!(type in numberOfPtsPerTraceType)) {
130-
numberOfPtsPerTraceType[type] = 0
131-
}
130+
// cap the number of points using a budget
131+
maxPtBudget += len / maxPtsPerTraceType(type)
132+
if (maxPtBudget > 1) return true
132133

133-
numberOfPtsPerTraceType[type] += estimateDataLength(trace)
134+
// other special cases
134135

135-
// cap the number of point per trace type
136-
if (numberOfPtsPerTraceType[type] > maxPtsPerTraceType(type)) {
136+
// box with boxpoints: 'all'
137+
if (
138+
type === 'box' &&
139+
trace.boxpoints === 'all' &&
140+
len > 5e4
141+
) {
137142
return true
138143
}
139144

140-
// other special cases
145+
// violin with points: 'all'
146+
if (
147+
type === 'violin' &&
148+
trace.points === 'all' &&
149+
len > 5e4
150+
) {
151+
return true
152+
}
141153

142154
// mesh3d will `alphahull` and 1000+ pts
143155
if (
144156
type === 'mesh3d' &&
145157
'alphahull' in trace && Number(trace.alphahull) >= 0 &&
146-
findMaxArrayLength(trace) > 1000
158+
len > 1000
147159
) {
148160
return true
149161
}
@@ -155,11 +167,23 @@ function willFigureHang (result) {
155167
// can be (much) smaller than the true number of points plotted
156168
// when it does not match the length of the other coordinate arrays.
157169
function findMaxArrayLength (cont) {
158-
const lengths = Object.keys(cont)
170+
const arrays = Object.keys(cont)
159171
.filter(k => Array.isArray(cont[k]))
160-
.map(k => cont[k].length)
172+
.map(k => cont[k])
161173

162-
return Math.max(...lengths)
174+
const lengths = arrays.map(arr => {
175+
const innerArrays = arr.filter(Array.isArray)
176+
177+
if (innerArrays.length) {
178+
return innerArrays
179+
.map(a => a.length)
180+
.reduce((a, v) => a + v)
181+
} else {
182+
return arr.length
183+
}
184+
})
185+
186+
return arrays.length ? Math.max(...lengths) : 0
163187
}
164188

165189
function estimateDataLength (trace) {
@@ -200,7 +224,7 @@ function maxPtsPerTraceType (type) {
200224
case 'histogram2dcontour':
201225
case 'box':
202226
case 'violin':
203-
return 1e5
227+
return 1e6
204228

205229
default:
206230
return 5e4

test/unit/plotly-graph_test.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,25 @@ tap.test('parse:', t => {
376376
})
377377
})
378378

379+
t.test('failing heatmap case', t => {
380+
var z = [
381+
new Array(5e5),
382+
new Array(5e5),
383+
new Array(5e5)
384+
]
385+
386+
fn({
387+
data: [{
388+
type: 'heatmap',
389+
z: z
390+
}]
391+
}, {}, (errorCode, result) => {
392+
t.equal(errorCode, 400, 'code')
393+
t.type(result.msg, 'string', 'msg type')
394+
t.end()
395+
})
396+
})
397+
379398
t.test('failing case from too many traces', t => {
380399
var data = new Array(3e3)
381400

@@ -388,6 +407,34 @@ tap.test('parse:', t => {
388407
})
389408
})
390409

410+
t.test('failing edge case (box with boxpoints all)', t => {
411+
fn({
412+
data: [{
413+
type: 'box',
414+
x: new Array(1e5),
415+
boxpoints: 'all'
416+
}]
417+
}, {}, (errorCode, result) => {
418+
t.equal(errorCode, 400, 'code')
419+
t.type(result.msg, 'string', 'msg type')
420+
t.end()
421+
})
422+
})
423+
424+
t.test('failing edge case (violin with points all)', t => {
425+
fn({
426+
data: [{
427+
type: 'violin',
428+
x: new Array(1e5),
429+
points: 'all'
430+
}]
431+
}, {}, (errorCode, result) => {
432+
t.equal(errorCode, 400, 'code')
433+
t.type(result.msg, 'string', 'msg type')
434+
t.end()
435+
})
436+
})
437+
391438
t.test('failing edge case (mesh3d and alphahull)', t => {
392439
var data = new Array(2e3)
393440

@@ -404,6 +451,50 @@ tap.test('parse:', t => {
404451
})
405452
})
406453

454+
t.test('failing case from too many traces', t => {
455+
var data = new Array(3e3)
456+
457+
fn({
458+
data: data
459+
}, {}, (errorCode, result) => {
460+
t.equal(errorCode, 400, 'code')
461+
t.type(result.msg, 'string', 'msg type')
462+
t.end()
463+
})
464+
})
465+
466+
t.test('failing edge case (to test budget)', t => {
467+
fn({
468+
data: [{
469+
type: 'scatter',
470+
x: new Array(4e4) // below 5e4 threshold
471+
}, {
472+
type: 'heatmap',
473+
z: [
474+
new Array(5e5), // below 5e4 threshold
475+
new Array(4e5)
476+
]
477+
}]
478+
}, {}, (errorCode, result) => {
479+
t.equal(errorCode, 400, 'code')
480+
t.type(result.msg, 'string', 'msg type')
481+
t.end()
482+
})
483+
})
484+
485+
t.test('failing case (with no arrays in starting trace)', t => {
486+
fn({
487+
data: [{}, {}, {
488+
type: 'scatter',
489+
x: new Array(1e6)
490+
}]
491+
}, {}, (errorCode, result) => {
492+
t.equal(errorCode, 400, 'code')
493+
t.type(result.msg, 'string', 'msg type')
494+
t.end()
495+
})
496+
})
497+
407498
t.end()
408499
})
409500

0 commit comments

Comments
 (0)