-
Notifications
You must be signed in to change notification settings - Fork 633
/
Copy pathplotly.js
156 lines (147 loc) · 5.7 KB
/
plotly.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
HTMLWidgets.widget({
name: "plotly",
type: "output",
initialize: function(el, width, height) {
return {};
},
resize: function(el, width, height, instance) {
if (instance.autosize) {
Plotly.relayout(el.id, {width: width, height: height});
}
},
renderValue: function(el, x, instance) {
var shinyMode;
if (typeof(window) !== "undefined") {
// make sure plots don't get created outside the network
window.PLOTLYENV = window.PLOTLYENV || {};
window.PLOTLYENV.BASE_URL = x.base_url;
shinyMode = !!window.Shiny;
}
var graphDiv = document.getElementById(el.id);
// if no plot exists yet, create one with a particular configuration
if (!instance.plotly) {
Plotly.plot(graphDiv, x.data, x.layout, x.config);
instance.plotly = true;
instance.autosize = x.layout.autosize;
} else {
// Can we do smooth transitions of x/y locations of points?
var doTransition = x.data.length === graphDiv._fullData.length;
for (i = 0; i < x.data.length; i++) {
var type = x.data[i].type || "scatter";
doTransition = doTransition &&
x.data[i].x.length === graphDiv._fullData[i].x.length &&
x.data[i].y.length === graphDiv._fullData[i].y.length &&
type === "scatter" && x.data[i].mode === "markers";
}
if (!doTransition) {
Plotly.newPlot(graphDiv, x.data, x.layout);
} else {
// construct x/y scales from graphDiv
var lay = graphDiv._fullLayout;
var xDom = lay.xaxis.range;
var yDom = lay.yaxis.range;
var xRng = [0, lay.width - lay.margin.l - lay.margin.r];
var yRng = [lay.height - lay.margin.t - lay.margin.b, 0];
// TODO: does this generalize to non-linear scales?
var xaxis = Plotly.d3.scale.linear().domain(xDom).range(xRng);
var yaxis = Plotly.d3.scale.linear().domain(yDom).range(yRng);
// store new x/y positions as an array of objects (for D3 bind)
x.transitionDat = [];
for (i = 0; i < x.data.length; i++) {
var d = x.data[i];
for (j = 0; j < d.x.length; j++) {
x.transitionDat.push({x: d.x[j], y: d.y[j]});
}
}
// we call newPlot() with old x/y locations and transition to new ones
for (i = 0; i < x.data.length; i++) {
x.data[i].xNew = x.data[i].x;
x.data[i].yNew = x.data[i].y;
x.data[i].x = graphDiv._fullData[i].x;
x.data[i].y = graphDiv._fullData[i].y;
}
Plotly.newPlot(graphDiv, x.data, x.layout);
// attempt to transition when appropriate
window.requestAnimationFrame(function() {
var pts = Plotly.d3.selectAll('.scatterlayer .point');
if (pts[0].length > 0) {
// Transition the transform --
// https://gist.github.com/mbostock/1642874
pts
.data(x.transitionDat)
.transition()
// TODO: provide arguments to these options!!
.duration(0)
.ease('linear')
.attr('transform', function(d) {
return 'translate(' + xaxis(d.x) + ',' + yaxis(d.y) + ')';
});
// update graphDiv data
for (i = 0; i < x.data.length; i++) {
graphDiv._fullData[i].x = x.data[i].xNew;
graphDiv._fullData[i].y = x.data[i].yNew;
}
}
});
}
}
sendEventData = function(eventType) {
return function(eventData) {
if (eventData === undefined || !eventData.hasOwnProperty("points")) {
return null;
}
var d = eventData.points.map(function(pt) {
var obj = {
curveNumber: pt.curveNumber,
pointNumber: pt.pointNumber,
x: pt.x,
y: pt.y
};
// grab the trace corresponding to this point
var tr = x.data[pt.curveNumber];
// add on additional trace info, if it exists
attachKey = function(keyName) {
if (tr.hasOwnProperty(keyName) && tr[keyName] !== null) {
if (typeof pt.pointNumber === "number") {
obj[keyName] = tr[keyName][pt.pointNumber];
} else {
obj[keyName] = tr[keyName][pt.pointNumber[0]][pt.pointNumber[1]];
}// TODO: can pointNumber be 3D?
}
};
attachKey("z");
attachKey("key");
return obj;
});
Shiny.onInputChange(
".clientValue-" + eventType + "-" + x.source,
JSON.stringify(d)
);
};
};
// send user input event data to shiny
if (shinyMode) {
// https://plot.ly/javascript/zoom-events/
graphDiv.on('plotly_relayout', function(d) {
Shiny.onInputChange(
".clientValue-" + "plotly_relayout" + "-" + x.source,
JSON.stringify(d)
);
});
graphDiv.on('plotly_hover', sendEventData('plotly_hover'));
graphDiv.on('plotly_click', sendEventData('plotly_click'));
graphDiv.on('plotly_selected', sendEventData('plotly_selected'));
graphDiv.on('plotly_unhover', function(eventData) {
Shiny.onInputChange(".clientValue-plotly_hover-" + x.source, null);
});
graphDiv.on('plotly_doubleclick', function(eventData) {
Shiny.onInputChange(".clientValue-plotly_click-" + x.source, null);
});
// 'plotly_deselect' is code for doubleclick when in select mode
graphDiv.on('plotly_deselect', function(eventData) {
Shiny.onInputChange(".clientValue-plotly_selected-" + x.source, null);
Shiny.onInputChange(".clientValue-plotly_click-" + x.source, null);
});
}
}
});