-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathlayout_attributes.js
327 lines (286 loc) · 10.7 KB
/
layout_attributes.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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/**
* 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 colorAttrs = require('../../components/color/attributes');
var axesAttrs = require('../cartesian/layout_attributes');
var extendFlat = require('../../lib').extendFlat;
var overrideAll = require('../../plot_api/edit_types').overrideAll;
var domainItem = {
valType: 'info_array',
role: 'info',
editType: 'plot',
items: [
{valType: 'number', min: 0, max: 1},
{valType: 'number', min: 0, max: 1}
],
dflt: [0, 1]
};
var axisLineGridAttr = overrideAll({
color: axesAttrs.color,
showline: extendFlat({}, axesAttrs.showline, {dflt: true}),
linecolor: axesAttrs.linecolor,
linewidth: axesAttrs.linewidth,
showgrid: extendFlat({}, axesAttrs.showgrid, {dflt: true}),
gridcolor: axesAttrs.gridcolor,
gridwidth: axesAttrs.gridwidth
// should we add zeroline* attributes?
// might be useful on radial axes where range is negative and positive
// we could add spike* attributes down the road
}, 'plot', 'from-root');
var axisTickAttr = overrideAll({
tickmode: axesAttrs.tickmode,
nticks: axesAttrs.nticks,
tick0: axesAttrs.tick0,
dtick: axesAttrs.dtick,
tickvals: axesAttrs.tickvals,
ticktext: axesAttrs.ticktext,
ticks: axesAttrs.ticks,
ticklen: axesAttrs.ticklen,
tickwidth: axesAttrs.tickwidth,
tickcolor: axesAttrs.tickcolor,
showticklabels: axesAttrs.showticklabels,
showtickprefix: axesAttrs.showtickprefix,
tickprefix: axesAttrs.tickprefix,
showticksuffix: axesAttrs.showticksuffix,
ticksuffix: axesAttrs.ticksuffix,
showexponent: axesAttrs.showexponent,
exponentformat: axesAttrs.exponentformat,
separatethousands: axesAttrs.separatethousands,
tickfont: axesAttrs.tickfont,
tickangle: axesAttrs.tickangle,
tickformat: axesAttrs.tickformat,
tickformatstops: axesAttrs.tickformatstops,
}, 'plot', 'from-root');
var radialAxisAttrs = {
visible: extendFlat({}, axesAttrs.visible, {dflt: true}),
type: axesAttrs.type,
// You thought maybe that range should only be a 'max' instead
// as it always starts at 0? But, looks like off-zero cutout polar chart are
// a thing:
// -> mpl allow radial ranges to start off 0
// -> same for matlab: https://www.mathworks.com/help/matlab/ref/rlim.html
autorange: axesAttrs.autorange,
// might make 'nonnegative' the default,
// or add a special polar algo.
rangemode: axesAttrs.rangemode,
range: axesAttrs.range,
categoryorder: axesAttrs.categoryorder,
categoryarray: axesAttrs.categoryarray,
// position (name analogous to xaxis.position),
// or maybe something more specific e.g. angle angleoffset?
//
// (should this support any data coordinate system?)
// I think it is more intuitive to set this as just an angle!
// Thoughts?
position: {
valType: 'angle',
editType: 'plot',
role: 'info',
description: [
'Sets the angle (in degrees) from which the radial axis is drawn.',
'Note that by default, radial axis line on the theta=0 line',
'corresponds to a line pointing right (like what mathematicians prefer).',
'Defaults to the first `polar.sector` angle.'
].join(' ')
},
side: {
valType: 'enumerated',
// maybe 'clockwise' and 'counterclockwise' would be best here
values: ['left', 'right'],
dflt: 'right',
editType: 'plot',
role: 'info',
description: [
'Determines on which side of radial axis line',
'the tick and tick labels appear.'
].join(' ')
},
// not sure about these
// maybe just for radialaxis ??
title: axesAttrs.title,
titlefont: axesAttrs.titlefont,
// only applies to radial axis for now (i.e. for cliponaxis: false traces)
// but angular.layer could be a thing later
layer: axesAttrs.layer,
hoverformat: axesAttrs.hoverformat,
// More attributes:
// We'll need some attribute that determines the span
// to draw donut-like charts
// e.g. https://github.com/matplotlib/matplotlib/issues/4217
//
// maybe something like 'span' or 'hole' (like pie, but pie set it in data coords?)
// span: {},
// hole: 1
// maybe should add a boolean to enable square grid lines
// and square axis lines
// (most common in radar-like charts)
// e.g. squareline/squaregrid or showline/showgrid: 'square' (on-top of true)
editType: 'calc'
};
extendFlat(
radialAxisAttrs,
// N.B. the radialaxis grid lines are circular,
// but radialaxis lines are straight from circle center to outer bound
axisLineGridAttr,
axisTickAttr
);
var angularAxisAttrs = {
visible: extendFlat({}, axesAttrs.visible, {dflt: true}),
type: {
valType: 'enumerated',
// 'linear' should maybe be called 'angle' or 'angular' here
// to make clear that axis here is periodic and more tightly match
// `thetaunit`?
//
// no 'log' for now
values: ['-', 'linear', 'date', 'category'],
dflt: '-',
role: 'info',
editType: 'calc',
description: [
'Sets the angular axis type.',
'If *linear*, set `thetaunit` to determine the unit in which axis value are shown.',
'If *date*, set `period` to determine the wrap around period.',
'If *category, set `period` to determine the number of integer coordinates around polar axis.'
].join(' ')
},
categoryorder: axesAttrs.categoryorder,
categoryarray: axesAttrs.categoryarray,
thetaunit: {
valType: 'enumerated',
values: ['radians', 'degrees'],
dflt: 'degrees',
role: 'info',
editType: 'calc',
description: [
'Sets the format unit of the formatted *theta* values.',
'Has an effect only when `angularaxis.type` is *linear*.'
].join(' ')
},
period: {
valType: 'any',
editType: 'calc',
role: 'info',
description: ''
// 360 / 2*pi for linear (might not need to set it)
// and to full range for other types
// 'period' is the angular equivalent to 'range'
// similar to dtick, one way to achieve e.g.:
// - period that equals the timeseries length
// http://flowingdata.com/2017/01/24/one-dataset-visualized-25-ways/18-polar-coordinates/
// - and 1-year periods (focusing on seasonal change0
// http://otexts.org/fpp2/seasonal-plots.html
// https://blogs.scientificamerican.com/sa-visual/why-are-so-many-babies-born-around-8-00-a-m/
// http://www.seasonaladjustment.com/2012/09/05/clock-plot-visualising-seasonality-using-r-and-ggplot2-part-3/
// https://i.pinimg.com/736x/49/b9/72/49b972ccb3206a1a6d6f870dac543280.jpg
// https://www.climate-lab-book.ac.uk/spirals/
},
direction: {
valType: 'enumerated',
values: ['counterclockwise', 'clockwise'],
// we could make the default 'clockwise' for date axes ...
dflt: 'counterclockwise',
role: 'info',
editType: 'calc',
description: [
'Sets the direction corresponding to positive angles.'
].join(' ')
},
// matlab uses thetaZeroLocation: 'North', 'West', 'East', 'South'
// mpl uses set_theta_zero_location('W', offset=10)
//
// position is analogous to yaxis.position, but as an angle (going
// counterclockwise about cartesian y=0.
position: {
valType: 'angle',
// we could maybe make `position: 90` by default for category and date angular axes.
dflt: 0,
editType: 'calc',
role: 'info',
description: [
'Sets that start position (in degrees) of the angular axis',
'Note that by default, polar subplots are orientation such that the theta=0',
'corresponds to a line pointing right (like what mathematicians prefer).',
'For example to make the angular axis start from the North (like on a compass),',
'set `angularaxis.position` to *90*.'
].join(' ')
},
hoverformat: axesAttrs.hoverformat,
editType: 'calc'
};
extendFlat(
angularAxisAttrs,
// N.B. the angular grid lines are straight lines from circle center to outer bound
// the angular line is circular bounding the polar plot area.
axisLineGridAttr,
// Note that ticksuffix defaults to '°' for angular axes with `thetaunit: 'degrees'`
axisTickAttr
);
module.exports = {
// AJ and I first thought about a x/y/zoom system for paper-based zooming
// but I came to think that sector span + radial axis range
// zooming will be better
//
// TODO confirm with team.
// x: {},
// y: {},
// zoom: {},
domain: {
x: extendFlat({}, domainItem, {
description: [
'Sets the horizontal domain of this subplot',
'(in plot fraction).'
].join(' ')
}),
y: extendFlat({}, domainItem, {
description: [
'Sets the vertical domain of this subplot',
'(in plot fraction).'
].join(' ')
}),
editType: 'plot'
},
// Maybe this should angularaxis.range correspond to
// angular span of the drawing area?
//
// matlab's angular equivalent to 'range' bounds the drawing area
// (partial circles as they call it)
// https://www.mathworks.com/help/matlab/ref/thetalim.html
//
// as this attribute would be best set in (absolute) angles,
// I think this should be set outside of angularaxis e.g
// as polar.sector: [0, 180]
sector: {
valType: 'info_array',
items: [
// or be more strict -> `valType: 'angle' with `dflt: [0, 360]`
{valType: 'number', editType: 'plot'},
{valType: 'number', editType: 'plot'}
],
dflt: [0, 360],
role: 'info',
editType: 'plot',
description: [
'Sets angular span of this polar subplot with two angles (in degrees).',
'Sector are assumed to be spanned in the counterclockwise direction',
'with *0* corresponding to rightmost limit of the polar subplot.'
].join(' ')
},
bgcolor: {
valType: 'color',
role: 'style',
editType: 'plot',
dflt: colorAttrs.background,
description: 'Set the background color of the subplot'
},
radialaxis: radialAxisAttrs,
angularaxis: angularAxisAttrs,
// TODO maybe?
// annotations:
editType: 'calc'
};