Skip to content

Commit a7451ff

Browse files
committed
Merge pull request #240 from plotly/gl-hover
Trigger hover and click events on gl3d graphs
2 parents 5d35fd6 + fa12603 commit a7451ff

File tree

8 files changed

+188
-44
lines changed

8 files changed

+188
-44
lines changed

devtools/test_dashboard/test_gl3d.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ plots['projection-traces'] = require('@mocks/gl3d_projection-traces.json');
2424
plots['opacity-scaling-spikes'] = require('@mocks/gl3d_opacity-scaling-spikes.json');
2525
plots['text-weirdness'] = require('@mocks/gl3d_text-weirdness.json');
2626
plots['wire-surface'] = require('@mocks/gl3d_wire-surface.json');
27-
plots['triangle-mesh3d'] = require('@mocks/gl3d_triangle.json');
27+
plots['triangle'] = require('@mocks/gl3d_triangle.json');
2828
plots['snowden'] = require('@mocks/gl3d_snowden.json');
2929
plots['bunny'] = require('@mocks/gl3d_bunny.json');
3030
plots['ribbons'] = require('@mocks/gl3d_ribbons.json');
31-
plots['scatter-time'] = require('@mocks/gl3d_scatter-date.json');
31+
plots['scatter-date'] = require('@mocks/gl3d_scatter-date.json');
3232
plots['cufflinks'] = require('@mocks/gl3d_cufflinks.json');
3333
plots['chrisp-nan-1'] = require('@mocks/gl3d_chrisp-nan-1.json');
3434
plots['marker-arrays'] = require('@mocks/gl3d_marker-arrays.json');

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"gl-mat4": "^1.1.2",
5858
"gl-mesh3d": "^1.0.7",
5959
"gl-plot2d": "^1.1.6",
60-
"gl-plot3d": "^1.3.0",
60+
"gl-plot3d": "^1.5.0",
6161
"gl-scatter2d": "^1.0.5",
6262
"gl-scatter2d-fancy": "^1.1.1",
6363
"gl-scatter3d": "^1.0.4",

src/plots/gl3d/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ exports.plot = function plotGl3d(gd) {
5151
// If Scene is not instantiated, create one!
5252
if(scene === undefined) {
5353
scene = new Scene({
54-
container: gd.querySelector('.gl-container'),
5554
id: sceneId,
55+
graphDiv: gd,
56+
container: gd.querySelector('.gl-container'),
5657
staticPlot: gd._context.staticPlot,
5758
plotGlPixelRatio: gd._context.plotGlPixelRatio
5859
},

src/plots/gl3d/scene.js

+49-16
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ var STATIC_CANVAS, STATIC_CONTEXT;
3434

3535
function render(scene) {
3636

37-
//Update size of svg container
37+
// update size of svg container
3838
var svgContainer = scene.svgContainer;
3939
var clientRect = scene.container.getBoundingClientRect();
4040
var width = clientRect.width, height = clientRect.height;
@@ -45,7 +45,7 @@ function render(scene) {
4545
computeTickMarks(scene);
4646
scene.glplot.axes.update(scene.axesOptions);
4747

48-
//Check if pick has changed
48+
// check if pick has changed
4949
var keys = Object.keys(scene.traces);
5050
var lastPicked = null;
5151
var selection = scene.glplot.selection;
@@ -59,22 +59,28 @@ function render(scene) {
5959
}
6060

6161
function formatter(axisName, val) {
62-
if(val === undefined) return undefined;
6362
if(typeof val === 'string') return val;
6463

6564
var axis = scene.fullSceneLayout[axisName];
6665
return Axes.tickText(axis, axis.c2l(val), 'hover').text;
6766
}
6867

68+
var oldEventData;
69+
6970
if(lastPicked !== null) {
7071
var pdata = project(scene.glplot.cameraParams, selection.dataCoordinate),
71-
hoverinfo = lastPicked.data.hoverinfo;
72+
trace = lastPicked.data,
73+
hoverinfo = trace.hoverinfo;
74+
75+
var xVal = formatter('xaxis', selection.traceCoordinate[0]),
76+
yVal = formatter('yaxis', selection.traceCoordinate[1]),
77+
zVal = formatter('zaxis', selection.traceCoordinate[2]);
7278

7379
if(hoverinfo !== 'all') {
7480
var hoverinfoParts = hoverinfo.split('+');
75-
if(hoverinfoParts.indexOf('x') === -1) selection.traceCoordinate[0] = undefined;
76-
if(hoverinfoParts.indexOf('y') === -1) selection.traceCoordinate[1] = undefined;
77-
if(hoverinfoParts.indexOf('z') === -1) selection.traceCoordinate[2] = undefined;
81+
if(hoverinfoParts.indexOf('x') === -1) xVal = undefined;
82+
if(hoverinfoParts.indexOf('y') === -1) yVal = undefined;
83+
if(hoverinfoParts.indexOf('z') === -1) zVal = undefined;
7884
if(hoverinfoParts.indexOf('text') === -1) selection.textLabel = undefined;
7985
if(hoverinfoParts.indexOf('name') === -1) lastPicked.name = undefined;
8086
}
@@ -83,18 +89,42 @@ function render(scene) {
8389
Fx.loneHover({
8490
x: (0.5 + 0.5 * pdata[0] / pdata[3]) * width,
8591
y: (0.5 - 0.5 * pdata[1] / pdata[3]) * height,
86-
xLabel: formatter('xaxis', selection.traceCoordinate[0]),
87-
yLabel: formatter('yaxis', selection.traceCoordinate[1]),
88-
zLabel: formatter('zaxis', selection.traceCoordinate[2]),
92+
xLabel: xVal,
93+
yLabel: yVal,
94+
zLabel: zVal,
8995
text: selection.textLabel,
9096
name: lastPicked.name,
9197
color: lastPicked.color
9298
}, {
9399
container: svgContainer
94100
});
95101
}
102+
103+
var eventData = {
104+
points: [{
105+
x: xVal,
106+
y: yVal,
107+
z: zVal,
108+
data: trace._input,
109+
fullData: trace,
110+
curveNumber: trace.index,
111+
pointNumber: selection.data.index
112+
}]
113+
};
114+
115+
if(selection.buttons && selection.distance < 5) {
116+
scene.graphDiv.emit('plotly_click', eventData);
117+
}
118+
else {
119+
scene.graphDiv.emit('plotly_hover', eventData);
120+
}
121+
122+
oldEventData = eventData;
123+
}
124+
else {
125+
Fx.loneUnhover(svgContainer);
126+
scene.graphDiv.emit('plotly_unhover', oldEventData);
96127
}
97-
else Fx.loneUnhover(svgContainer);
98128
}
99129

100130
function initializeGLPlot(scene, fullLayout, canvas, gl) {
@@ -110,9 +140,9 @@ function initializeGLPlot(scene, fullLayout, canvas, gl) {
110140
autoBounds: false
111141
};
112142

113-
//For static plots, we reuse the WebGL context as WebKit doesn't collect them
114-
//reliably
115-
if (scene.staticMode) {
143+
// for static plots, we reuse the WebGL context
144+
// as WebKit doesn't collect them reliably
145+
if(scene.staticMode) {
116146
if(!STATIC_CONTEXT) {
117147
STATIC_CANVAS = document.createElement('canvas');
118148
try {
@@ -178,11 +208,14 @@ function initializeGLPlot(scene, fullLayout, canvas, gl) {
178208

179209
function Scene(options, fullLayout) {
180210

181-
//Create sub container for plot
211+
// create sub container for plot
182212
var sceneContainer = document.createElement('div');
183213
var plotContainer = options.container;
184214

185-
//Create SVG container for hover text
215+
// keep a ref to the graph div to fire hover+click events
216+
this.graphDiv = options.graphDiv;
217+
218+
// create SVG container for hover text
186219
var svgContainer = document.createElementNS(
187220
'http://www.w3.org/2000/svg',
188221
'svg');

src/plots/plots.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -476,11 +476,10 @@ plots.supplyDefaults = function(gd) {
476476
};
477477

478478
function cleanScenes(newFullLayout, oldFullLayout) {
479-
var oldSceneKey,
480-
oldSceneKeys = plots.getSubplotIds(oldFullLayout, 'gl3d');
479+
var oldSceneKeys = plots.getSubplotIds(oldFullLayout, 'gl3d');
481480

482-
for (var i = 0; i < oldSceneKeys.length; i++) {
483-
oldSceneKey = oldSceneKeys[i];
481+
for(var i = 0; i < oldSceneKeys.length; i++) {
482+
var oldSceneKey = oldSceneKeys[i];
484483
if(!newFullLayout[oldSceneKey] && !!oldFullLayout[oldSceneKey]._scene) {
485484
oldFullLayout[oldSceneKey]._scene.destroy();
486485
}

test/jasmine/assets/mouse_event.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
module.exports = function(type, x, y) {
2-
var options = {
1+
module.exports = function(type, x, y, opts) {
2+
var fullOpts = {
33
bubbles: true,
44
clientX: x,
55
clientY: y
66
};
77

8+
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
9+
if(opts && opts.buttons) {
10+
fullOpts.buttons = opts.buttons;
11+
}
12+
813
var el = document.elementFromPoint(x,y);
9-
var ev = new window.MouseEvent(type, options);
14+
var ev = new window.MouseEvent(type, fullOpts);
1015
el.dispatchEvent(ev);
1116
};

test/jasmine/karma.conf.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ func.defaultConfig = {
3939
testFileGlob
4040
],
4141

42-
// list of files to exclude
43-
exclude: [
44-
],
42+
exclude: [],
4543

4644
// preprocess matching files before serving them to the browser
4745
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor

0 commit comments

Comments
 (0)