Skip to content

Commit b49e13f

Browse files
committed
add hovertemplate to gl3d traces
- i.e. scatter3d, surface, mesh3d, cone, streamtube and isosurface!
1 parent 02e51d8 commit b49e13f

File tree

19 files changed

+134
-46
lines changed

19 files changed

+134
-46
lines changed

src/components/fx/hovertemplate_attributes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = function(opts, extra) {
3232
role: 'info',
3333
dflt: '',
3434
arrayOk: true,
35-
editType: 'none',
35+
editType: opts.editType || 'none',
3636
description: [
3737
'Template string used for rendering the information that appear on hover box.',
3838
'Note that this will override `hoverinfo`.',

src/plots/gl3d/scene.js

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,21 @@ function render(scene) {
7171
var pdata = project(scene.glplot.cameraParams, selection.dataCoordinate);
7272
trace = lastPicked.data;
7373
var ptNumber = selection.index;
74-
var hoverinfo = Fx.castHoverinfo(trace, scene.fullLayout, ptNumber);
75-
var hoverinfoParts = hoverinfo.split('+');
76-
var isHoverinfoAll = hoverinfo === 'all';
7774

78-
var xVal = formatter('xaxis', selection.traceCoordinate[0]);
79-
var yVal = formatter('yaxis', selection.traceCoordinate[1]);
80-
var zVal = formatter('zaxis', selection.traceCoordinate[2]);
75+
var labels = {
76+
xLabel: formatter('xaxis', selection.traceCoordinate[0]),
77+
yLabel: formatter('yaxis', selection.traceCoordinate[1]),
78+
zLabel: formatter('zaxis', selection.traceCoordinate[2])
79+
};
80+
81+
var hoverinfo = Fx.castHoverinfo(trace, scene.fullLayout, ptNumber);
82+
var hoverinfoParts = (hoverinfo || '').split('+');
83+
var isHoverinfoAll = hoverinfo && hoverinfo === 'all';
8184

82-
if(!isHoverinfoAll) {
83-
if(hoverinfoParts.indexOf('x') === -1) xVal = undefined;
84-
if(hoverinfoParts.indexOf('y') === -1) yVal = undefined;
85-
if(hoverinfoParts.indexOf('z') === -1) zVal = undefined;
85+
if(!trace.hovertemplate && !isHoverinfoAll) {
86+
if(hoverinfoParts.indexOf('x') === -1) labels.xLabel = undefined;
87+
if(hoverinfoParts.indexOf('y') === -1) labels.yLabel = undefined;
88+
if(hoverinfoParts.indexOf('z') === -1) labels.zLabel = undefined;
8689
if(hoverinfoParts.indexOf('text') === -1) selection.textLabel = undefined;
8790
if(hoverinfoParts.indexOf('name') === -1) lastPicked.name = undefined;
8891
}
@@ -91,27 +94,38 @@ function render(scene) {
9194
var vectorTx = [];
9295

9396
if(trace.type === 'cone' || trace.type === 'streamtube') {
97+
labels.uLabel = formatter('xaxis', selection.traceCoordinate[3]);
9498
if(isHoverinfoAll || hoverinfoParts.indexOf('u') !== -1) {
95-
vectorTx.push('u: ' + formatter('xaxis', selection.traceCoordinate[3]));
99+
vectorTx.push('u: ' + labels.uLabel);
96100
}
101+
102+
labels.vLabel = formatter('yaxis', selection.traceCoordinate[4]);
97103
if(isHoverinfoAll || hoverinfoParts.indexOf('v') !== -1) {
98-
vectorTx.push('v: ' + formatter('yaxis', selection.traceCoordinate[4]));
104+
vectorTx.push('v: ' + labels.vLabel);
99105
}
106+
107+
labels.wLabel = formatter('zaxis', selection.traceCoordinate[5]);
100108
if(isHoverinfoAll || hoverinfoParts.indexOf('w') !== -1) {
101-
vectorTx.push('w: ' + formatter('zaxis', selection.traceCoordinate[5]));
109+
vectorTx.push('w: ' + labels.wLabel);
102110
}
111+
112+
labels.normLabel = selection.traceCoordinate[6].toPrecision(3);
103113
if(isHoverinfoAll || hoverinfoParts.indexOf('norm') !== -1) {
104-
vectorTx.push('norm: ' + selection.traceCoordinate[6].toPrecision(3));
114+
vectorTx.push('norm: ' + labels.normLabel);
105115
}
106-
if(trace.type === 'streamtube' && (isHoverinfoAll || hoverinfoParts.indexOf('divergence') !== -1)) {
107-
vectorTx.push('divergence: ' + selection.traceCoordinate[7].toPrecision(3));
116+
if(trace.type === 'streamtube') {
117+
labels.divergenceLabel = selection.traceCoordinate[7].toPrecision(3);
118+
if(isHoverinfoAll || hoverinfoParts.indexOf('divergence') !== -1) {
119+
vectorTx.push('divergence: ' + labels.divergenceLabel);
120+
}
108121
}
109122
if(selection.textLabel) {
110123
vectorTx.push(selection.textLabel);
111124
}
112125
tx = vectorTx.join('<br>');
113126
} else if(trace.type === 'isosurface') {
114-
vectorTx.push('value: ' + Axes.tickText(scene.mockAxis, scene.mockAxis.d2l(selection.traceCoordinate[3]), 'hover').text);
127+
labels.valueLabel = Axes.tickText(scene.mockAxis, scene.mockAxis.d2l(selection.traceCoordinate[3]), 'hover').text;
128+
vectorTx.push('value: ' + labels.valueLabel);
115129
if(selection.textLabel) {
116130
vectorTx.push(selection.textLabel);
117131
}
@@ -120,27 +134,6 @@ function render(scene) {
120134
tx = selection.textLabel;
121135
}
122136

123-
if(scene.fullSceneLayout.hovermode) {
124-
Fx.loneHover({
125-
x: (0.5 + 0.5 * pdata[0] / pdata[3]) * width,
126-
y: (0.5 - 0.5 * pdata[1] / pdata[3]) * height,
127-
xLabel: xVal,
128-
yLabel: yVal,
129-
zLabel: zVal,
130-
text: tx,
131-
name: lastPicked.name,
132-
color: Fx.castHoverOption(trace, ptNumber, 'bgcolor') || lastPicked.color,
133-
borderColor: Fx.castHoverOption(trace, ptNumber, 'bordercolor'),
134-
fontFamily: Fx.castHoverOption(trace, ptNumber, 'font.family'),
135-
fontSize: Fx.castHoverOption(trace, ptNumber, 'font.size'),
136-
fontColor: Fx.castHoverOption(trace, ptNumber, 'font.color')
137-
}, {
138-
container: svgContainer,
139-
gd: scene.graphDiv
140-
});
141-
}
142-
143-
// TODO not sure if streamtube x/y/z should be emitted as x/y/z
144137
var pointData = {
145138
x: selection.traceCoordinate[0],
146139
y: selection.traceCoordinate[1],
@@ -151,18 +144,41 @@ function render(scene) {
151144
pointNumber: ptNumber
152145
};
153146

147+
Fx.appendArrayPointValue(pointData, trace, ptNumber);
148+
154149
if(trace._module.eventData) {
155150
pointData = trace._module.eventData(pointData, selection, trace, {}, ptNumber);
156151
}
157152

158-
Fx.appendArrayPointValue(pointData, trace, ptNumber);
159-
160153
var eventData = {points: [pointData]};
161154

155+
if(scene.fullSceneLayout.hovermode) {
156+
Fx.loneHover({
157+
trace: trace,
158+
x: (0.5 + 0.5 * pdata[0] / pdata[3]) * width,
159+
y: (0.5 - 0.5 * pdata[1] / pdata[3]) * height,
160+
xLabel: labels.xLabel,
161+
yLabel: labels.yLabel,
162+
zLabel: labels.zLabel,
163+
text: tx,
164+
name: lastPicked.name,
165+
color: Fx.castHoverOption(trace, ptNumber, 'bgcolor') || lastPicked.color,
166+
borderColor: Fx.castHoverOption(trace, ptNumber, 'bordercolor'),
167+
fontFamily: Fx.castHoverOption(trace, ptNumber, 'font.family'),
168+
fontSize: Fx.castHoverOption(trace, ptNumber, 'font.size'),
169+
fontColor: Fx.castHoverOption(trace, ptNumber, 'font.color'),
170+
hovertemplate: Lib.castOption(trace, ptNumber, 'hovertemplate'),
171+
hovertemplateLabels: Lib.extendFlat({}, pointData, labels),
172+
eventData: [pointData]
173+
}, {
174+
container: svgContainer,
175+
gd: scene.graphDiv
176+
});
177+
}
178+
162179
if(selection.buttons && selection.distance < 5) {
163180
scene.graphDiv.emit('plotly_click', eventData);
164-
}
165-
else {
181+
} else {
166182
scene.graphDiv.emit('plotly_hover', eventData);
167183
}
168184

src/traces/cone/attributes.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
var colorscaleAttrs = require('../../components/colorscale/attributes');
1212
var colorbarAttrs = require('../../components/colorbar/attributes');
13+
var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
1314
var mesh3dAttrs = require('../mesh3d/attributes');
1415
var baseAttrs = require('../../plots/attributes');
1516

@@ -157,7 +158,8 @@ var attrs = {
157158
'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,',
158159
'these elements will be seen in the hover labels.'
159160
].join(' ')
160-
}
161+
},
162+
hovertemplate: hovertemplateAttrs({editType: 'calc'}, {keys: ['norm']})
161163
};
162164

163165
extendFlat(attrs, colorscaleAttrs('', {

src/traces/cone/defaults.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
5252
colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'c'});
5353

5454
coerce('text');
55+
coerce('hovertemplate');
5556

5657
// disable 1D transforms (for now)
5758
traceOut._length = null;

src/traces/cone/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ module.exports = {
2222
},
2323
calc: require('./calc'),
2424
plot: require('./convert'),
25+
eventData: function(out, pt) {
26+
out.norm = pt.traceCoordinate[6];
27+
return out;
28+
},
2529

2630
meta: {
2731
description: [

src/traces/isosurface/attributes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
var colorscaleAttrs = require('../../components/colorscale/attributes');
1212
var colorbarAttrs = require('../../components/colorbar/attributes');
13+
var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
1314
var surfaceAtts = require('../surface/attributes');
1415
var meshAttrs = require('../mesh3d/attributes');
1516
var baseAttrs = require('../../plots/attributes');
@@ -226,6 +227,7 @@ var attrs = module.exports = overrideAll(extendFlat({
226227
'these elements will be seen in the hover labels.'
227228
].join(' ')
228229
},
230+
hovertemplate: hovertemplateAttrs()
229231
},
230232

231233
colorscaleAttrs('', {

src/traces/isosurface/defaults.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

1211
var Registry = require('../../registry');
@@ -85,6 +84,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
8584
// Coerce remaining properties
8685
[
8786
'text',
87+
'hovertemplate',
8888
'lighting.ambient',
8989
'lighting.diffuse',
9090
'lighting.specular',

src/traces/mesh3d/attributes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
var colorscaleAttrs = require('../../components/colorscale/attributes');
1212
var colorbarAttrs = require('../../components/colorbar/attributes');
13+
var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
1314
var surfaceAtts = require('../surface/attributes');
1415
var baseAttrs = require('../../plots/attributes');
1516

@@ -89,6 +90,7 @@ module.exports = extendFlat({
8990
'these elements will be seen in the hover labels.'
9091
].join(' ')
9192
},
93+
hovertemplate: hovertemplateAttrs({editType: 'calc'}),
9294

9395
delaunayaxis: {
9496
valType: 'enumerated',

src/traces/mesh3d/defaults.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
8888
}
8989

9090
coerce('text');
91+
coerce('hovertemplate');
9192

9293
// disable 1D transforms
9394
// x/y/z should match lengths, and i/j/k should match as well, but

src/traces/scatter3d/attributes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
var scatterAttrs = require('../scatter/attributes');
1212
var colorAttributes = require('../../components/colorscale/attributes');
13+
var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
1314
var baseAttrs = require('../../plots/attributes');
1415
var DASHES = require('../../constants/gl3d_dashes');
1516

@@ -94,6 +95,7 @@ var attrs = module.exports = overrideAll({
9495
'To be seen, trace `hoverinfo` must contain a *text* flag.'
9596
].join(' ')
9697
}),
98+
hovertemplate: hovertemplateAttrs(),
9799

98100
mode: extendFlat({}, scatterAttrs.mode, // shouldn't this be on-par with 2D?
99101
{dflt: 'lines+markers'}),

0 commit comments

Comments
 (0)