Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1c6144e

Browse files
committedMar 21, 2018
pull out 'convert' logic in scattergl/convert.js
1 parent f9a15be commit 1c6144e

File tree

5 files changed

+469
-389
lines changed

5 files changed

+469
-389
lines changed
 

‎src/constants/gl2d_dashes.js

Lines changed: 0 additions & 19 deletions
This file was deleted.

‎src/traces/scattergl/attributes.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ var plotAttrs = require('../../plots/attributes');
1212
var scatterAttrs = require('../scatter/attributes');
1313
var colorAttrs = require('../../components/colorscale/color_attributes');
1414

15-
var DASHES = require('../../constants/gl2d_dashes');
1615
var extendFlat = require('../../lib/extend').extendFlat;
1716
var overrideAll = require('../../plot_api/edit_types').overrideAll;
17+
var DASHES = require('./constants').DASHES;
1818

19-
var scatterLineAttrs = scatterAttrs.line,
20-
scatterMarkerAttrs = scatterAttrs.marker,
21-
scatterMarkerLineAttrs = scatterMarkerAttrs.line;
19+
var scatterLineAttrs = scatterAttrs.line;
20+
var scatterMarkerAttrs = scatterAttrs.marker;
21+
var scatterMarkerLineAttrs = scatterMarkerAttrs.line;
2222

2323
var attrs = module.exports = overrideAll({
2424
x: scatterAttrs.x,

‎src/traces/scattergl/constants.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var SYMBOL_SIZE = 20;
12+
13+
module.exports = {
14+
TOO_MANY_POINTS: 1e5,
15+
16+
SYMBOL_SDF_SIZE: 200,
17+
SYMBOL_SIZE: SYMBOL_SIZE,
18+
SYMBOL_STROKE: SYMBOL_SIZE / 20,
19+
20+
DOT_RE: /-dot/,
21+
OPEN_RE: /-open/,
22+
23+
DASHES: {
24+
solid: [1],
25+
dot: [1, 1],
26+
dash: [4, 1],
27+
longdash: [8, 1],
28+
dashdot: [4, 1, 1, 1],
29+
longdashdot: [8, 1, 1, 1]
30+
}
31+
};

‎src/traces/scattergl/convert.js

Lines changed: 397 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,397 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var svgSdf = require('svg-path-sdf');
12+
var rgba = require('color-normalize');
13+
14+
var Registry = require('../../registry');
15+
var Lib = require('../../lib');
16+
var Drawing = require('../../components/drawing');
17+
var AxisIDs = require('../../plots/cartesian/axis_ids');
18+
19+
var formatColor = require('../../lib/gl_format_color');
20+
var subTypes = require('../scatter/subtypes');
21+
var makeBubbleSizeFn = require('../scatter/make_bubble_size_func');
22+
23+
var constants = require('./constants');
24+
25+
function convertStyle(gd, trace) {
26+
var i;
27+
28+
var opts = {
29+
marker: null,
30+
line: null,
31+
fill: null,
32+
errorX: null,
33+
errorY: null,
34+
selected: null,
35+
unselected: null
36+
};
37+
38+
if(trace.visible !== true) return opts;
39+
40+
if(subTypes.hasMarkers(trace)) {
41+
opts.marker = convertMarkerStyle(trace);
42+
opts.selected = convertMarkerSelection(trace, trace.selected);
43+
opts.unselected = convertMarkerSelection(trace, trace.unselected);
44+
}
45+
46+
if(subTypes.hasLines(trace)) {
47+
opts.line = {
48+
overlay: true,
49+
thickness: trace.line.width,
50+
color: trace.line.color,
51+
opacity: trace.opacity
52+
};
53+
54+
var dashes = (constants.DASHES[trace.line.dash] || [1]).slice();
55+
for(i = 0; i < dashes.length; ++i) {
56+
dashes[i] *= trace.line.width;
57+
}
58+
opts.line.dashes = dashes;
59+
}
60+
61+
if(trace.error_x && trace.error_x.visible) {
62+
opts.errorX = convertErrorBarStyle(trace, trace.error_x);
63+
}
64+
65+
if(trace.error_y && trace.error_y.visible) {
66+
opts.errorY = convertErrorBarStyle(trace, trace.error_y);
67+
}
68+
69+
if(!!trace.fill && trace.fill !== 'none') {
70+
opts.fill = {
71+
closed: true,
72+
fill: trace.fillcolor,
73+
thickness: 0
74+
};
75+
}
76+
77+
return opts;
78+
}
79+
80+
function convertMarkerStyle(trace) {
81+
var count = trace._length || (trace.dimensions || [])._length;
82+
var optsIn = trace.marker;
83+
var optsOut = {};
84+
var i;
85+
86+
var multiSymbol = Array.isArray(optsIn.symbol);
87+
var multiColor = Lib.isArrayOrTypedArray(optsIn.color);
88+
var multiLineColor = Lib.isArrayOrTypedArray(optsIn.line.color);
89+
var multiOpacity = Lib.isArrayOrTypedArray(optsIn.opacity);
90+
var multiSize = Lib.isArrayOrTypedArray(optsIn.size);
91+
var multiLineWidth = Lib.isArrayOrTypedArray(optsIn.line.width);
92+
93+
var isOpen;
94+
if(!multiSymbol) isOpen = constants.OPEN_RE.test(optsIn.symbol);
95+
96+
// prepare colors
97+
if(multiSymbol || multiColor || multiLineColor || multiOpacity) {
98+
optsOut.colors = new Array(count);
99+
optsOut.borderColors = new Array(count);
100+
101+
var colors = formatColor(optsIn, optsIn.opacity, count);
102+
var borderColors = formatColor(optsIn.line, optsIn.opacity, count);
103+
104+
if(!Array.isArray(borderColors[0])) {
105+
var borderColor = borderColors;
106+
borderColors = Array(count);
107+
for(i = 0; i < count; i++) {
108+
borderColors[i] = borderColor;
109+
}
110+
}
111+
if(!Array.isArray(colors[0])) {
112+
var color = colors;
113+
colors = Array(count);
114+
for(i = 0; i < count; i++) {
115+
colors[i] = color;
116+
}
117+
}
118+
119+
optsOut.colors = colors;
120+
optsOut.borderColors = borderColors;
121+
122+
for(i = 0; i < count; i++) {
123+
if(multiSymbol) {
124+
var symbol = optsIn.symbol[i];
125+
isOpen = constants.OPEN_RE.test(symbol);
126+
}
127+
if(isOpen) {
128+
borderColors[i] = colors[i].slice();
129+
colors[i] = colors[i].slice();
130+
colors[i][3] = 0;
131+
}
132+
}
133+
134+
optsOut.opacity = trace.opacity;
135+
} else {
136+
if(isOpen) {
137+
optsOut.color = rgba(optsIn.color, 'uint8');
138+
optsOut.color[3] = 0;
139+
optsOut.borderColor = rgba(optsIn.color, 'uint8');
140+
} else {
141+
optsOut.color = rgba(optsIn.color, 'uint8');
142+
optsOut.borderColor = rgba(optsIn.line.color, 'uint8');
143+
}
144+
145+
optsOut.opacity = trace.opacity * optsIn.opacity;
146+
}
147+
148+
// prepare symbols
149+
if(multiSymbol) {
150+
optsOut.markers = new Array(count);
151+
for(i = 0; i < count; i++) {
152+
optsOut.markers[i] = getSymbolSdf(optsIn.symbol[i]);
153+
}
154+
} else {
155+
optsOut.marker = getSymbolSdf(optsIn.symbol);
156+
}
157+
158+
// prepare sizes
159+
var markerSizeFunc = makeBubbleSizeFn(trace);
160+
var s;
161+
162+
if(multiSize || multiLineWidth) {
163+
var sizes = optsOut.sizes = new Array(count);
164+
var borderSizes = optsOut.borderSizes = new Array(count);
165+
var sizeTotal = 0;
166+
var sizeAvg;
167+
168+
if(multiSize) {
169+
for(i = 0; i < count; i++) {
170+
sizes[i] = markerSizeFunc(optsIn.size[i]);
171+
sizeTotal += sizes[i];
172+
}
173+
sizeAvg = sizeTotal / count;
174+
} else {
175+
s = markerSizeFunc(optsIn.size);
176+
for(i = 0; i < count; i++) {
177+
sizes[i] = s;
178+
}
179+
}
180+
181+
// See https://github.com/plotly/plotly.js/pull/1781#discussion_r121820798
182+
if(multiLineWidth) {
183+
for(i = 0; i < count; i++) {
184+
borderSizes[i] = markerSizeFunc(optsIn.line.width[i]);
185+
}
186+
} else {
187+
s = markerSizeFunc(optsIn.line.width);
188+
for(i = 0; i < count; i++) {
189+
borderSizes[i] = s;
190+
}
191+
}
192+
193+
optsOut.sizeAvg = sizeAvg;
194+
} else {
195+
optsOut.size = markerSizeFunc(optsIn && optsIn.size || 10);
196+
optsOut.borderSizes = markerSizeFunc(optsIn.line.width);
197+
}
198+
199+
return optsOut;
200+
}
201+
202+
function convertMarkerSelection(trace, target) {
203+
var optsIn = trace.marker;
204+
var optsOut = {};
205+
206+
if(!target) return optsOut;
207+
208+
if(target.marker && target.marker.symbol) {
209+
optsOut = convertMarkerStyle(Lib.extendFlat({}, optsIn, target.marker));
210+
} else if(target.marker) {
211+
if(target.marker.size) optsOut.sizes = target.marker.size;
212+
if(target.marker.color) optsOut.colors = target.marker.color;
213+
if(target.marker.opacity !== undefined) optsOut.opacity = target.marker.opacity;
214+
}
215+
216+
return optsOut;
217+
}
218+
219+
function convertErrorBarStyle(trace, target) {
220+
var optsOut = {
221+
capSize: target.width * 2,
222+
lineWidth: target.thickness,
223+
color: target.color
224+
};
225+
226+
if(target.copy_ystyle) {
227+
optsOut = trace.error_y;
228+
}
229+
230+
return optsOut;
231+
}
232+
233+
var SYMBOL_SDF_SIZE = constants.SYMBOL_SDF_SIZE;
234+
var SYMBOL_SIZE = constants.SYMBOL_SIZE;
235+
var SYMBOL_STROKE = constants.SYMBOL_STROKE;
236+
var SYMBOL_SDF = {};
237+
var SYMBOL_SVG_CIRCLE = Drawing.symbolFuncs[0](SYMBOL_SIZE * 0.05);
238+
239+
function getSymbolSdf(symbol) {
240+
if(symbol === 'circle') return null;
241+
242+
var symbolPath, symbolSdf;
243+
var symbolNumber = Drawing.symbolNumber(symbol);
244+
var symbolFunc = Drawing.symbolFuncs[symbolNumber % 100];
245+
var symbolNoDot = !!Drawing.symbolNoDot[symbolNumber % 100];
246+
var symbolNoFill = !!Drawing.symbolNoFill[symbolNumber % 100];
247+
248+
var isDot = constants.DOT_RE.test(symbol);
249+
250+
// get symbol sdf from cache or generate it
251+
if(SYMBOL_SDF[symbol]) return SYMBOL_SDF[symbol];
252+
253+
if(isDot && !symbolNoDot) {
254+
symbolPath = symbolFunc(SYMBOL_SIZE * 1.1) + SYMBOL_SVG_CIRCLE;
255+
}
256+
else {
257+
symbolPath = symbolFunc(SYMBOL_SIZE);
258+
}
259+
260+
symbolSdf = svgSdf(symbolPath, {
261+
w: SYMBOL_SDF_SIZE,
262+
h: SYMBOL_SDF_SIZE,
263+
viewBox: [-SYMBOL_SIZE, -SYMBOL_SIZE, SYMBOL_SIZE, SYMBOL_SIZE],
264+
stroke: symbolNoFill ? SYMBOL_STROKE : -SYMBOL_STROKE
265+
});
266+
SYMBOL_SDF[symbol] = symbolSdf;
267+
268+
return symbolSdf || null;
269+
}
270+
271+
function convertLinePositions(gd, trace, positions) {
272+
var count = positions.length / 2;
273+
var linePositions;
274+
var i;
275+
276+
if(subTypes.hasLines(trace) && count) {
277+
if(trace.line.shape === 'hv') {
278+
linePositions = [];
279+
for(i = 0; i < count - 1; i++) {
280+
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
281+
linePositions.push(NaN);
282+
linePositions.push(NaN);
283+
linePositions.push(NaN);
284+
linePositions.push(NaN);
285+
}
286+
else {
287+
linePositions.push(positions[i * 2]);
288+
linePositions.push(positions[i * 2 + 1]);
289+
linePositions.push(positions[i * 2 + 2]);
290+
linePositions.push(positions[i * 2 + 1]);
291+
}
292+
}
293+
linePositions.push(positions[positions.length - 2]);
294+
linePositions.push(positions[positions.length - 1]);
295+
} else if(trace.line.shape === 'vh') {
296+
linePositions = [];
297+
for(i = 0; i < count - 1; i++) {
298+
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
299+
linePositions.push(NaN);
300+
linePositions.push(NaN);
301+
linePositions.push(NaN);
302+
linePositions.push(NaN);
303+
}
304+
else {
305+
linePositions.push(positions[i * 2]);
306+
linePositions.push(positions[i * 2 + 1]);
307+
linePositions.push(positions[i * 2]);
308+
linePositions.push(positions[i * 2 + 3]);
309+
}
310+
}
311+
linePositions.push(positions[positions.length - 2]);
312+
linePositions.push(positions[positions.length - 1]);
313+
} else {
314+
linePositions = positions;
315+
}
316+
}
317+
318+
// If we have data with gaps, we ought to use rect joins
319+
// FIXME: get rid of this
320+
var hasNaN = false;
321+
for(i = 0; i < linePositions.length; i++) {
322+
if(isNaN(linePositions[i])) {
323+
hasNaN = true;
324+
break;
325+
}
326+
}
327+
328+
var join = (hasNaN || linePositions.length > constants.TOO_MANY_POINTS) ? 'rect' :
329+
subTypes.hasMarkers(trace) ? 'rect' : 'round';
330+
331+
// fill gaps
332+
if(hasNaN && trace.connectgaps) {
333+
var lastX = linePositions[0];
334+
var lastY = linePositions[1];
335+
336+
for(i = 0; i < linePositions.length; i += 2) {
337+
if(isNaN(linePositions[i]) || isNaN(linePositions[i + 1])) {
338+
linePositions[i] = lastX;
339+
linePositions[i + 1] = lastY;
340+
}
341+
else {
342+
lastX = linePositions[i];
343+
lastY = linePositions[i + 1];
344+
}
345+
}
346+
}
347+
348+
return {
349+
join: join,
350+
positions: linePositions
351+
};
352+
}
353+
354+
function convertErrorBarPositions(gd, trace, positions) {
355+
var calcFromTrace = Registry.getComponentMethod('errorbars', 'calcFromTrace');
356+
var vals = calcFromTrace(trace, gd._fullLayout);
357+
var count = positions.length / 2;
358+
var out = {};
359+
360+
function put(axLetter) {
361+
var errors = new Float64Array(4 * count);
362+
var ax = AxisIDs.getFromId(gd, trace[axLetter + 'axis']);
363+
var pOffset = {x: 0, y: 1}[axLetter];
364+
var eOffset = {x: [0, 1, 2, 3], y: [2, 3, 0, 1]}[axLetter];
365+
366+
for(var i = 0, p = 0; i < count; i++, p += 4) {
367+
errors[p + eOffset[0]] = positions[i * 2 + pOffset] - ax.d2l(vals[i][axLetter + 's']) || 0;
368+
errors[p + eOffset[1]] = ax.d2l(vals[i][axLetter + 'h']) - positions[i * 2 + pOffset] || 0;
369+
errors[p + eOffset[2]] = 0;
370+
errors[p + eOffset[3]] = 0;
371+
}
372+
373+
return errors;
374+
}
375+
376+
377+
if(trace.error_x && trace.error_x.visible) {
378+
out.x = {
379+
positions: positions,
380+
errors: put('x')
381+
};
382+
}
383+
if(trace.error_y && trace.error_y.visible) {
384+
out.y = {
385+
positions: positions,
386+
errors: put('y')
387+
};
388+
}
389+
390+
return out;
391+
}
392+
393+
module.exports = {
394+
convertStyle: convertStyle,
395+
convertLinePositions: convertLinePositions,
396+
convertErrorBarPositions: convertErrorBarPositions
397+
};

‎src/traces/scattergl/index.js

Lines changed: 37 additions & 366 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,26 @@ var createScatter = require('regl-scatter2d');
1313
var createLine = require('regl-line2d');
1414
var createError = require('regl-error2d');
1515
var kdtree = require('kdgrass');
16-
var rgba = require('color-normalize');
17-
var svgSdf = require('svg-path-sdf');
1816
var arrayRange = require('array-range');
1917

2018
var Registry = require('../../registry');
2119
var Lib = require('../../lib');
2220
var AxisIDs = require('../../plots/cartesian/axis_ids');
23-
var Drawing = require('../../components/drawing');
24-
var formatColor = require('../../lib/gl_format_color');
2521

2622
var subTypes = require('../scatter/subtypes');
2723
var calcMarkerSize = require('../scatter/calc').calcMarkerSize;
2824
var calcAxisExpansion = require('../scatter/calc').calcAxisExpansion;
2925
var calcColorscales = require('../scatter/colorscale_calc');
30-
var makeBubbleSizeFn = require('../scatter/make_bubble_size_func');
3126
var linkTraces = require('../scatter/link_traces');
3227
var getTraceColor = require('../scatter/get_trace_color');
3328
var fillHoverText = require('../scatter/fill_hover_text');
3429

35-
var DASHES = require('../../constants/gl2d_dashes');
30+
var convertStyle = require('./convert').convertStyle;
31+
var convertLinePositions = require('./convert').convertLinePositions;
32+
var convertErrorBarPositions = require('./convert').convertErrorBarPositions;
33+
3634
var BADNUM = require('../../constants/numerical').BADNUM;
37-
var SYMBOL_SDF_SIZE = 200;
38-
var SYMBOL_SIZE = 20;
39-
var SYMBOL_STROKE = SYMBOL_SIZE / 20;
40-
var SYMBOL_SDF = {};
41-
var SYMBOL_SVG_CIRCLE = Drawing.symbolFuncs[0](SYMBOL_SIZE * 0.05);
42-
var TOO_MANY_POINTS = 1e5;
43-
var DOT_RE = /-dot/;
44-
var OPEN_RE = /-open/;
35+
var TOO_MANY_POINTS = require('./constants').TOO_MANY_POINTS;
4536

4637
function calc(gd, trace) {
4738
var fullLayout = gd._fullLayout;
@@ -91,37 +82,36 @@ function calc(gd, trace) {
9182

9283
// create scene options and scene
9384
calcColorscales(trace);
94-
var options = sceneOptions(gd, subplot, trace, positions);
95-
var markerOptions = options.marker;
85+
var opts = sceneOptions(gd, subplot, trace, positions);
9686
var scene = sceneUpdate(gd, subplot);
97-
var ppad;
9887

9988
// Re-use SVG scatter axis expansion routine except
10089
// for graph with very large number of points where it
10190
// performs poorly.
10291
// In big data case, fake Axes.expand outputs with data bounds,
10392
// and an average size for array marker.size inputs.
93+
var ppad;
10494
if(count < TOO_MANY_POINTS) {
10595
ppad = calcMarkerSize(trace, count);
106-
} else if(markerOptions) {
107-
ppad = 2 * (markerOptions.sizeAvg || Math.max(markerOptions.size, 3));
96+
} else if(opts.marker) {
97+
ppad = 2 * (opts.marker.sizeAvg || Math.max(opts.marker.size, 3));
10898
}
10999
calcAxisExpansion(gd, trace, xa, ya, x, y, ppad);
110100

111101
// set flags to create scene renderers
112-
if(options.fill && !scene.fill2d) scene.fill2d = true;
113-
if(options.marker && !scene.scatter2d) scene.scatter2d = true;
114-
if(options.line && !scene.line2d) scene.line2d = true;
115-
if((options.errorX || options.errorY) && !scene.error2d) scene.error2d = true;
116-
117-
// save scene options batch
118-
scene.lineOptions.push(options.line);
119-
scene.errorXOptions.push(options.errorX);
120-
scene.errorYOptions.push(options.errorY);
121-
scene.fillOptions.push(options.fill);
122-
scene.markerOptions.push(options.marker);
123-
scene.selectedOptions.push(options.selected);
124-
scene.unselectedOptions.push(options.unselected);
102+
if(opts.fill && !scene.fill2d) scene.fill2d = true;
103+
if(opts.marker && !scene.scatter2d) scene.scatter2d = true;
104+
if(opts.line && !scene.line2d) scene.line2d = true;
105+
if((opts.errorX || opts.errorY) && !scene.error2d) scene.error2d = true;
106+
107+
// save scene opts batch
108+
scene.lineOptions.push(opts.line);
109+
scene.errorXOptions.push(opts.errorX);
110+
scene.errorYOptions.push(opts.errorY);
111+
scene.fillOptions.push(opts.fill);
112+
scene.markerOptions.push(opts.marker);
113+
scene.selectedOptions.push(opts.selected);
114+
scene.unselectedOptions.push(opts.unselected);
125115
scene.count++;
126116

127117
// stash scene ref
@@ -138,318 +128,31 @@ function calc(gd, trace) {
138128

139129
// create scene options
140130
function sceneOptions(gd, subplot, trace, positions) {
141-
var fullLayout = gd._fullLayout;
142-
var count = positions.length / 2;
143-
var markerOpts = trace.marker;
144-
var i;
145-
146-
var hasLines, hasErrorX, hasErrorY, hasMarkers, hasFill;
131+
var opts = convertStyle(gd, trace);
147132

148-
if(trace.visible !== true) {
149-
hasLines = false;
150-
hasErrorX = false;
151-
hasErrorY = false;
152-
hasMarkers = false;
153-
hasFill = false;
154-
} else {
155-
hasLines = subTypes.hasLines(trace) && positions.length > 1;
156-
hasErrorX = trace.error_x && trace.error_x.visible === true;
157-
hasErrorY = trace.error_y && trace.error_y.visible === true;
158-
hasMarkers = subTypes.hasMarkers(trace);
159-
hasFill = !!trace.fill && trace.fill !== 'none';
133+
if(opts.marker) {
134+
opts.marker.positions = positions;
160135
}
161136

162-
var lineOptions, markerOptions, fillOptions;
163-
var errorXOptions, errorYOptions;
164-
var selectedOptions, unselectedOptions;
165-
var linePositions;
166-
167-
if(hasErrorX || hasErrorY) {
168-
var calcFromTrace = Registry.getComponentMethod('errorbars', 'calcFromTrace');
169-
var errorVals = calcFromTrace(trace, fullLayout);
170-
171-
if(hasErrorX) {
172-
errorXOptions = makeErrorOptions('x', trace.error_x, errorVals);
173-
}
174-
if(hasErrorY) {
175-
errorYOptions = makeErrorOptions('y', trace.error_y, errorVals);
176-
}
137+
if(opts.line && positions.length > 1) {
138+
Lib.extendFlat(
139+
opts.line,
140+
convertLinePositions(gd, trace, positions)
141+
);
177142
}
178143

179-
if(hasLines) {
180-
lineOptions = {};
181-
lineOptions.thickness = trace.line.width;
182-
lineOptions.color = trace.line.color;
183-
lineOptions.opacity = trace.opacity;
184-
lineOptions.overlay = true;
185-
186-
var dashes = (DASHES[trace.line.dash] || [1]).slice();
187-
for(i = 0; i < dashes.length; ++i) dashes[i] *= lineOptions.thickness;
188-
lineOptions.dashes = dashes;
189-
190-
if(trace.line.shape === 'hv') {
191-
linePositions = [];
192-
for(i = 0; i < Math.floor(positions.length / 2) - 1; i++) {
193-
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
194-
linePositions.push(NaN);
195-
linePositions.push(NaN);
196-
linePositions.push(NaN);
197-
linePositions.push(NaN);
198-
}
199-
else {
200-
linePositions.push(positions[i * 2]);
201-
linePositions.push(positions[i * 2 + 1]);
202-
linePositions.push(positions[i * 2 + 2]);
203-
linePositions.push(positions[i * 2 + 1]);
204-
}
205-
}
206-
linePositions.push(positions[positions.length - 2]);
207-
linePositions.push(positions[positions.length - 1]);
208-
}
209-
else if(trace.line.shape === 'vh') {
210-
linePositions = [];
211-
for(i = 0; i < Math.floor(positions.length / 2) - 1; i++) {
212-
if(isNaN(positions[i * 2]) || isNaN(positions[i * 2 + 1])) {
213-
linePositions.push(NaN);
214-
linePositions.push(NaN);
215-
linePositions.push(NaN);
216-
linePositions.push(NaN);
217-
}
218-
else {
219-
linePositions.push(positions[i * 2]);
220-
linePositions.push(positions[i * 2 + 1]);
221-
linePositions.push(positions[i * 2]);
222-
linePositions.push(positions[i * 2 + 3]);
223-
}
224-
}
225-
linePositions.push(positions[positions.length - 2]);
226-
linePositions.push(positions[positions.length - 1]);
227-
}
228-
else {
229-
linePositions = positions;
230-
}
144+
if(opts.errorX || opts.errorY) {
145+
var errors = convertErrorBarPositions(gd, trace, positions);
231146

232-
// If we have data with gaps, we ought to use rect joins
233-
// FIXME: get rid of this
234-
var hasNaN = false;
235-
for(i = 0; i < linePositions.length; i++) {
236-
if(isNaN(linePositions[i])) {
237-
hasNaN = true;
238-
break;
239-
}
147+
if(opts.errorX) {
148+
Lib.extendFlat(opts.errorX, errors.x);
240149
}
241-
242-
lineOptions.join = (hasNaN || linePositions.length > TOO_MANY_POINTS) ? 'rect' :
243-
hasMarkers ? 'rect' : 'round';
244-
245-
// fill gaps
246-
if(hasNaN && trace.connectgaps) {
247-
var lastX = linePositions[0], lastY = linePositions[1];
248-
for(i = 0; i < linePositions.length; i += 2) {
249-
if(isNaN(linePositions[i]) || isNaN(linePositions[i + 1])) {
250-
linePositions[i] = lastX;
251-
linePositions[i + 1] = lastY;
252-
}
253-
else {
254-
lastX = linePositions[i];
255-
lastY = linePositions[i + 1];
256-
}
257-
}
150+
if(opts.errorY) {
151+
Lib.extendFlat(opts.errorY, errors.y);
258152
}
259-
260-
lineOptions.positions = linePositions;
261-
}
262-
263-
if(hasFill) {
264-
fillOptions = {};
265-
fillOptions.fill = trace.fillcolor;
266-
fillOptions.thickness = 0;
267-
fillOptions.closed = true;
268153
}
269154

270-
if(hasMarkers) {
271-
markerOptions = makeMarkerOptions(markerOpts);
272-
selectedOptions = makeSelectedOptions(trace.selected, markerOpts);
273-
unselectedOptions = makeSelectedOptions(trace.unselected, markerOpts);
274-
markerOptions.positions = positions;
275-
}
276-
277-
function makeErrorOptions(axLetter, errorOpts, vals) {
278-
var options = {};
279-
options.positions = positions;
280-
281-
var ax = AxisIDs.getFromId(gd, trace[axLetter + 'axis']);
282-
var errors = options.errors = new Float64Array(4 * count);
283-
var pOffset = {x: 0, y: 1}[axLetter];
284-
var eOffset = {x: [0, 1, 2, 3], y: [2, 3, 0, 1]}[axLetter];
285-
286-
for(var i = 0, p = 0; i < count; i++, p += 4) {
287-
errors[p + eOffset[0]] = positions[i * 2 + pOffset] - ax.d2l(vals[i][axLetter + 's']) || 0;
288-
errors[p + eOffset[1]] = ax.d2l(vals[i][axLetter + 'h']) - positions[i * 2 + pOffset] || 0;
289-
errors[p + eOffset[2]] = 0;
290-
errors[p + eOffset[3]] = 0;
291-
}
292-
293-
if(errorOpts.copy_ystyle) {
294-
errorOpts = trace.error_y;
295-
}
296-
297-
options.capSize = errorOpts.width * 2;
298-
options.lineWidth = errorOpts.thickness;
299-
options.color = errorOpts.color;
300-
301-
return options;
302-
}
303-
304-
function makeSelectedOptions(selected, markerOpts) {
305-
var options = {};
306-
307-
if(!selected) return options;
308-
309-
if(selected.marker && selected.marker.symbol) {
310-
options = makeMarkerOptions(Lib.extendFlat({}, markerOpts, selected.marker));
311-
}
312-
313-
// shortcut simple selection logic
314-
else {
315-
options = {};
316-
if(selected.marker.size) options.sizes = selected.marker.size;
317-
if(selected.marker.color) options.colors = selected.marker.color;
318-
if(selected.marker.opacity !== undefined) options.opacity = selected.marker.opacity;
319-
}
320-
321-
return options;
322-
}
323-
324-
function makeMarkerOptions(markerOpts) {
325-
var markerOptions = {};
326-
var i;
327-
328-
var multiSymbol = Array.isArray(markerOpts.symbol);
329-
var multiColor = Lib.isArrayOrTypedArray(markerOpts.color);
330-
var multiLineColor = Lib.isArrayOrTypedArray(markerOpts.line.color);
331-
var multiOpacity = Lib.isArrayOrTypedArray(markerOpts.opacity);
332-
var multiSize = Lib.isArrayOrTypedArray(markerOpts.size);
333-
var multiLineWidth = Lib.isArrayOrTypedArray(markerOpts.line.width);
334-
335-
var isOpen;
336-
if(!multiSymbol) isOpen = OPEN_RE.test(markerOpts.symbol);
337-
338-
// prepare colors
339-
if(multiSymbol || multiColor || multiLineColor || multiOpacity) {
340-
markerOptions.colors = new Array(count);
341-
markerOptions.borderColors = new Array(count);
342-
var colors = formatColor(markerOpts, markerOpts.opacity, count);
343-
var borderColors = formatColor(markerOpts.line, markerOpts.opacity, count);
344-
345-
if(!Array.isArray(borderColors[0])) {
346-
var borderColor = borderColors;
347-
borderColors = Array(count);
348-
for(i = 0; i < count; i++) {
349-
borderColors[i] = borderColor;
350-
}
351-
}
352-
if(!Array.isArray(colors[0])) {
353-
var color = colors;
354-
colors = Array(count);
355-
for(i = 0; i < count; i++) {
356-
colors[i] = color;
357-
}
358-
}
359-
360-
markerOptions.colors = colors;
361-
markerOptions.borderColors = borderColors;
362-
363-
for(i = 0; i < count; i++) {
364-
if(multiSymbol) {
365-
var symbol = markerOpts.symbol[i];
366-
isOpen = OPEN_RE.test(symbol);
367-
}
368-
if(isOpen) {
369-
borderColors[i] = colors[i].slice();
370-
colors[i] = colors[i].slice();
371-
colors[i][3] = 0;
372-
}
373-
}
374-
375-
markerOptions.opacity = trace.opacity;
376-
}
377-
else {
378-
if(isOpen) {
379-
markerOptions.color = rgba(markerOpts.color, 'uint8');
380-
markerOptions.color[3] = 0;
381-
markerOptions.borderColor = rgba(markerOpts.color, 'uint8');
382-
} else {
383-
markerOptions.color = rgba(markerOpts.color, 'uint8');
384-
markerOptions.borderColor = rgba(markerOpts.line.color, 'uint8');
385-
}
386-
387-
markerOptions.opacity = trace.opacity * markerOpts.opacity;
388-
}
389-
390-
// prepare symbols
391-
if(multiSymbol) {
392-
markerOptions.markers = new Array(count);
393-
for(i = 0; i < count; i++) {
394-
markerOptions.markers[i] = getSymbolSdf(markerOpts.symbol[i]);
395-
}
396-
} else {
397-
markerOptions.marker = getSymbolSdf(markerOpts.symbol);
398-
}
399-
400-
// prepare sizes
401-
var markerSizeFunc = makeBubbleSizeFn(trace);
402-
var s;
403-
404-
if(multiSize || multiLineWidth) {
405-
var sizes = markerOptions.sizes = new Array(count);
406-
var borderSizes = markerOptions.borderSizes = new Array(count);
407-
var sizeTotal = 0;
408-
var sizeAvg;
409-
410-
if(multiSize) {
411-
for(i = 0; i < count; i++) {
412-
sizes[i] = markerSizeFunc(markerOpts.size[i]);
413-
sizeTotal += sizes[i];
414-
}
415-
sizeAvg = sizeTotal / count;
416-
} else {
417-
s = markerSizeFunc(markerOpts.size);
418-
for(i = 0; i < count; i++) {
419-
sizes[i] = s;
420-
}
421-
}
422-
423-
// See https://github.com/plotly/plotly.js/pull/1781#discussion_r121820798
424-
if(multiLineWidth) {
425-
for(i = 0; i < count; i++) {
426-
borderSizes[i] = markerSizeFunc(markerOpts.line.width[i]);
427-
}
428-
} else {
429-
s = markerSizeFunc(markerOpts.line.width);
430-
for(i = 0; i < count; i++) {
431-
borderSizes[i] = s;
432-
}
433-
}
434-
435-
markerOptions.sizeAvg = sizeAvg;
436-
} else {
437-
markerOptions.size = markerSizeFunc(markerOpts && markerOpts.size || 10);
438-
markerOptions.borderSizes = markerSizeFunc(markerOpts.line.width);
439-
}
440-
441-
return markerOptions;
442-
}
443-
444-
return {
445-
line: lineOptions,
446-
marker: markerOptions,
447-
errorX: errorXOptions,
448-
errorY: errorYOptions,
449-
fill: fillOptions,
450-
selected: selectedOptions,
451-
unselected: unselectedOptions
452-
};
155+
return opts;
453156
}
454157

455158
// make sure scene exists on subplot, return it
@@ -624,38 +327,6 @@ function sceneUpdate(gd, subplot) {
624327
return scene;
625328
}
626329

627-
function getSymbolSdf(symbol) {
628-
if(symbol === 'circle') return null;
629-
630-
var symbolPath, symbolSdf;
631-
var symbolNumber = Drawing.symbolNumber(symbol);
632-
var symbolFunc = Drawing.symbolFuncs[symbolNumber % 100];
633-
var symbolNoDot = !!Drawing.symbolNoDot[symbolNumber % 100];
634-
var symbolNoFill = !!Drawing.symbolNoFill[symbolNumber % 100];
635-
636-
var isDot = DOT_RE.test(symbol);
637-
638-
// get symbol sdf from cache or generate it
639-
if(SYMBOL_SDF[symbol]) return SYMBOL_SDF[symbol];
640-
641-
if(isDot && !symbolNoDot) {
642-
symbolPath = symbolFunc(SYMBOL_SIZE * 1.1) + SYMBOL_SVG_CIRCLE;
643-
}
644-
else {
645-
symbolPath = symbolFunc(SYMBOL_SIZE);
646-
}
647-
648-
symbolSdf = svgSdf(symbolPath, {
649-
w: SYMBOL_SDF_SIZE,
650-
h: SYMBOL_SDF_SIZE,
651-
viewBox: [-SYMBOL_SIZE, -SYMBOL_SIZE, SYMBOL_SIZE, SYMBOL_SIZE],
652-
stroke: symbolNoFill ? SYMBOL_STROKE : -SYMBOL_STROKE
653-
});
654-
SYMBOL_SDF[symbol] = symbolSdf;
655-
656-
return symbolSdf || null;
657-
}
658-
659330
function plot(gd, subplot, cdata) {
660331
if(!cdata.length) return;
661332

0 commit comments

Comments
 (0)
Please sign in to comment.