-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathplot.js
236 lines (186 loc) · 8.11 KB
/
plot.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
var d3 = require('d3');
var Drawing = require('../../components/drawing');
var map1dArray = require('./map_1d_array');
var makepath = require('./makepath');
var orientText = require('./orient_text');
var svgTextUtils = require('../../lib/svg_text_utils');
module.exports = function plot(gd, plotinfo, cdcarpet) {
for(var i = 0; i < cdcarpet.length; i++) {
plotOne(gd, plotinfo, cdcarpet[i]);
}
};
function makeg(el, type, klass) {
var join = el.selectAll(type + '.' + klass).data([0]);
join.enter().append(type).classed(klass, true);
return join;
}
function plotOne(gd, plotinfo, cd) {
var t = cd[0];
var trace = cd[0].trace,
xa = plotinfo.xaxis,
ya = plotinfo.yaxis,
aax = trace.aaxis,
bax = trace.baxis,
fullLayout = gd._fullLayout;
// uid = trace.uid,
// id = 'carpet' + uid;
var gridLayer = plotinfo.plot.selectAll('.carpetlayer');
var clipLayer = makeg(fullLayout._defs, 'g', 'clips');
var axisLayer = makeg(gridLayer, 'g', 'carpet' + trace.uid).classed('trace', true);
var minorLayer = makeg(axisLayer, 'g', 'minorlayer');
var majorLayer = makeg(axisLayer, 'g', 'majorlayer');
var boundaryLayer = makeg(axisLayer, 'g', 'boundarylayer');
var labelLayer = makeg(axisLayer, 'g', 'labellayer');
axisLayer.style('opacity', trace.opacity);
drawGridLines(xa, ya, majorLayer, aax, 'a', aax._gridlines, true);
drawGridLines(xa, ya, majorLayer, bax, 'b', bax._gridlines, true);
drawGridLines(xa, ya, minorLayer, aax, 'a', aax._minorgridlines, true);
drawGridLines(xa, ya, minorLayer, bax, 'b', bax._minorgridlines, true);
// NB: These are not ommitted if the lines are not active. The joins must be executed
// in order for them to get cleaned up without a full redraw
drawGridLines(xa, ya, boundaryLayer, aax, 'a-boundary', aax._boundarylines);
drawGridLines(xa, ya, boundaryLayer, bax, 'b-boundary', bax._boundarylines);
var maxAExtent = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, aax._labels, 'a-label');
var maxBExtent = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, bax._labels, 'b-label');
drawAxisTitles(gd, labelLayer, trace, t, xa, ya, maxAExtent, maxBExtent);
// Swap for debugging in order to draw directly:
// drawClipPath(trace, axisLayer, xa, ya);
drawClipPath(trace, t, clipLayer, xa, ya);
}
function drawClipPath(trace, t, layer, xaxis, yaxis) {
var seg, xp, yp, i;
// var clip = makeg(layer, 'g', 'carpetclip');
trace.clipPathId = 'clip' + trace.uid + 'carpet';
var clip = layer.select('#' + trace.clipPathId);
if(!clip.size()) {
clip = layer.append('clipPath')
.classed('carpetclip', true);
}
var path = makeg(clip, 'path', 'carpetboundary');
var segments = t.clipsegments;
var segs = [];
for(i = 0; i < segments.length; i++) {
seg = segments[i];
xp = map1dArray([], seg.x, xaxis.c2p);
yp = map1dArray([], seg.y, yaxis.c2p);
segs.push(makepath(xp, yp, seg.bicubic));
}
// This could be optimized ever so slightly to avoid no-op L segments
// at the corners, but it's so negligible that I don't think it's worth
// the extra complexity
trace.clipPathData = 'M' + segs.join('L') + 'Z';
clip.attr('id', trace.clipPathId);
path.attr('d', trace.clipPathData);
// .style('stroke-width', 20)
// .style('vector-effect', 'non-scaling-stroke')
// .style('stroke', 'black')
// .style('fill', 'rgba(0, 0, 0, 0.1)');
}
function drawGridLines(xaxis, yaxis, layer, axis, axisLetter, gridlines) {
var lineClass = 'const-' + axisLetter + '-lines';
var gridJoin = layer.selectAll('.' + lineClass).data(gridlines);
gridJoin.enter().append('path')
.classed(lineClass, true)
.style('vector-effect', 'non-scaling-stroke');
gridJoin.each(function(d) {
var gridline = d;
var x = gridline.x;
var y = gridline.y;
var xp = map1dArray([], x, xaxis.c2p);
var yp = map1dArray([], y, yaxis.c2p);
var path = 'M' + makepath(xp, yp, gridline.smoothing);
var el = d3.select(this);
el.attr('d', path)
.style('stroke-width', gridline.width)
.style('stroke', gridline.color)
.style('fill', 'none');
});
gridJoin.exit().remove();
}
function drawAxisLabels(gd, xaxis, yaxis, trace, t, layer, labels, labelClass) {
var labelJoin = layer.selectAll('text.' + labelClass).data(labels);
labelJoin.enter().append('text')
.classed(labelClass, true);
var maxExtent = 0;
labelJoin.each(function(label) {
// Most of the positioning is done in calc_labels. Only the parts that depend upon
// the screen space representation of the x and y axes are here:
var orientation;
if(label.axis.tickangle === 'auto') {
orientation = orientText(trace, xaxis, yaxis, label.xy, label.dxy);
} else {
var angle = (label.axis.tickangle + 180.0) * Math.PI / 180.0;
orientation = orientText(trace, xaxis, yaxis, label.xy, [Math.cos(angle), Math.sin(angle)]);
}
var direction = (label.endAnchor ? -1 : 1) * orientation.flip;
var labelEl = d3.select(this)
.attr({
'text-anchor': direction > 0 ? 'start' : 'end',
'data-notex': 1
})
.text(label.text)
.call(svgTextUtils.convertToTspans, gd);
var bbox = Drawing.bBox(this);
labelEl.attr('transform',
// Translate to the correct point:
'translate(' + orientation.p[0] + ',' + orientation.p[1] + ') ' +
// Rotate to line up with grid line tangent:
'rotate(' + orientation.angle + ')' +
// Adjust the baseline and indentation:
'translate(' + label.axis.labelpadding * direction + ',' + bbox.height * 0.3 + ')'
)
.call(Drawing.font, label.font.family, label.font.size, label.font.color);
maxExtent = Math.max(maxExtent, bbox.width + label.axis.labelpadding);
});
labelJoin.exit().remove();
return maxExtent;
}
function drawAxisTitles(gd, layer, trace, t, xa, ya, maxAExtent, maxBExtent) {
var a, b, xy, dxy;
a = 0.5 * (trace.a[0] + trace.a[trace.a.length - 1]);
b = trace.b[0];
xy = trace.ab2xy(a, b, true);
dxy = trace.dxyda_rough(a, b);
drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.aaxis, xa, ya, maxAExtent, 'a-title');
a = trace.a[0];
b = 0.5 * (trace.b[0] + trace.b[trace.b.length - 1]);
xy = trace.ab2xy(a, b, true);
dxy = trace.dxydb_rough(a, b);
drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.baxis, xa, ya, maxBExtent, 'b-title');
}
function drawAxisTitle(gd, layer, trace, t, xy, dxy, axis, xa, ya, offset, labelClass) {
var data = [];
if(axis.title) data.push(axis.title);
var titleJoin = layer.selectAll('text.' + labelClass).data(data);
titleJoin.enter().append('text')
.classed(labelClass, true);
// There's only one, but we'll do it as a join so it's updated nicely:
titleJoin.each(function() {
var orientation = orientText(trace, xa, ya, xy, dxy);
if(['start', 'both'].indexOf(axis.showticklabels) === -1) {
offset = 0;
}
// In addition to the size of the labels, add on some extra padding:
offset += axis.titlefont.size + axis.titleoffset;
var el = d3.select(this);
el.text(axis.title || '')
.call(svgTextUtils.convertToTspans, gd)
.attr('transform',
'translate(' + orientation.p[0] + ',' + orientation.p[1] + ') ' +
'rotate(' + orientation.angle + ') ' +
'translate(0,' + offset + ')'
)
.classed('user-select-none', true)
.attr('text-anchor', 'middle')
.call(Drawing.font, axis.titlefont);
});
titleJoin.exit().remove();
}