Skip to content

Commit aa868bb

Browse files
authored
Merge pull request #7710 from degzhaus/degzhaus/add_quiver
Add quiver trace type for vector field visualization
2 parents 4b4d445 + 36b0e92 commit aa868bb

38 files changed

Lines changed: 3426 additions & 14 deletions

draftlogs/7710_add.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add `quiver` trace type to visualize vector fields using arrows [[#7710](https://github.com/plotly/plotly.js/pull/7710)], with thanks to @degzhaus for the contribution!

lib/index-strict.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Plotly.register([
4949
require('../src/traces/scatterpolargl/strict'),
5050
require('./barpolar'),
5151
require('./scattersmith'),
52+
require('./quiver'),
5253

5354
// components
5455
require('./calendars'),

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Plotly.register([
4949
require('./scatterpolargl'),
5050
require('./barpolar'),
5151
require('./scattersmith'),
52+
require('./quiver'),
5253

5354
// components
5455
require('./calendars'),

lib/quiver.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
module.exports = require('../src/traces/quiver');

src/components/legend/style.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ function getGradientDirection(reversescale, isRadial) {
684684
function getStyleGuide(d) {
685685
var trace = d[0].trace;
686686
var contours = trace.contours;
687-
var showLine = subTypes.hasLines(trace);
687+
var showLine = subTypes.hasLines(trace) || (trace.visible && trace.type === 'quiver');
688688
var showMarker = subTypes.hasMarkers(trace);
689689

690690
var showFill = trace.visible && trace.fill && trace.fill !== 'none';

src/components/modebar/manage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ function isSelectable(fullData) {
284284

285285
if(!trace._module || !trace._module.selectPoints) continue;
286286

287-
if(Registry.traceIs(trace, 'scatter-like')) {
287+
if(Registry.traceIs(trace, 'scatter-like') && trace.type !== 'quiver') {
288288
if(scatterSubTypes.hasMarkers(trace) || scatterSubTypes.hasText(trace)) {
289289
selectable = true;
290290
}

src/traces/quiver/attributes.js

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
'use strict';
2+
3+
var baseAttrs = require('../../plots/attributes');
4+
var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs;
5+
var axisHoverFormat = require('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
6+
var extendFlat = require('../../lib/extend').extendFlat;
7+
var colorScaleAttrs = require('../../components/colorscale/attributes');
8+
var dash = require('../../components/drawing/attributes').dash;
9+
var annotationAttrs = require('../../components/annotations/attributes');
10+
var scatterAttrs = require('../scatter/attributes');
11+
12+
var attrs = {
13+
x: {
14+
valType: 'data_array',
15+
editType: 'calc+clearAxisTypes',
16+
anim: true,
17+
description: 'Sets the x coordinates of the arrow locations.'
18+
},
19+
x0: scatterAttrs.x0,
20+
dx: scatterAttrs.dx,
21+
y: {
22+
valType: 'data_array',
23+
editType: 'calc+clearAxisTypes',
24+
anim: true,
25+
description: 'Sets the y coordinates of the arrow locations.'
26+
},
27+
y0: scatterAttrs.y0,
28+
dy: scatterAttrs.dy,
29+
u: {
30+
valType: 'data_array',
31+
editType: 'calc',
32+
anim: true,
33+
description: 'Sets the x components of the arrow vectors.'
34+
},
35+
v: {
36+
valType: 'data_array',
37+
editType: 'calc',
38+
anim: true,
39+
description: 'Sets the y components of the arrow vectors.'
40+
},
41+
anglemode: {
42+
valType: 'enumerated',
43+
values: ['paper', 'data'],
44+
dflt: 'axis',
45+
editType: 'calc',
46+
description: [
47+
'Sets the mode used to determine the angle of the arrow vectors.',
48+
'If *paper*, u/v are interpreted in pixel coordinates and the rendered vector angle',
49+
'does not change regardless of the axes scales.',
50+
'If *data*, u/v are interpreted in data coordinates and the rendered vector angle',
51+
'may change, e.g. if zooming in along a single axis'
52+
].join(' ')
53+
},
54+
sizemode: {
55+
valType: 'enumerated',
56+
values: ['scaled', 'raw'],
57+
editType: 'calc',
58+
dflt: 'scaled',
59+
description: [
60+
'Determines whether arrows are drawn according to their raw lengths,',
61+
'or scaled based on the maximum vector length and point density. Note: When `anglemode` is *data*',
62+
'arrows are alwyas scaled and `sizemode` *raw* is ignored.',
63+
].join(' ')
64+
},
65+
sizeref: {
66+
valType: 'number',
67+
min: 0,
68+
editType: 'calc',
69+
dflt: 1,
70+
description: [
71+
'Adjusts the arrow size scaling. The arrow length is determined by the vector norm multiplied by `sizeref`,',
72+
'optionally normalized when `sizemode` is *scaled* (`sizeref` is applied after scaling).'
73+
].join(' ')
74+
},
75+
anchor: {
76+
valType: 'enumerated',
77+
values: ['tip', 'tail', 'center'],
78+
dflt: 'tail',
79+
editType: 'calc',
80+
description: [
81+
'Sets the arrows\' anchor with respect to their (x,y) positions.',
82+
'Use *tail* to place (x,y) at the base, *tip* to place (x,y) at the head,',
83+
'or *center* to center the arrow on (x,y).'
84+
].join(' ')
85+
},
86+
xhoverformat: axisHoverFormat('x'),
87+
yhoverformat: axisHoverFormat('y'),
88+
uhoverformat: axisHoverFormat('u', 'noDate'),
89+
vhoverformat: axisHoverFormat('v', 'noDate'),
90+
91+
hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {
92+
flags: ['x', 'y', 'u', 'v', 'text', 'name'],
93+
dflt: 'all'
94+
}),
95+
hovertemplate: hovertemplateAttrs({}, {
96+
keys: ['x', 'y', 'u', 'v', 'text', 'name']
97+
}),
98+
99+
// Text and labels (shared with scatter)
100+
text: scatterAttrs.text,
101+
textposition: scatterAttrs.textposition,
102+
textfont: scatterAttrs.textfont,
103+
104+
// Marker: color, colorscale, arrowhead sizing, and line styling for arrows
105+
marker: extendFlat(
106+
{
107+
arrowsize: extendFlat({}, annotationAttrs.arrowsize, {
108+
editType: 'calc',
109+
description: [
110+
'Sets the size of the arrow head relative to `marker.line.width`.',
111+
'A value of 1 (default) gives a head about 3x as wide as the line.'
112+
].join(' ')
113+
}),
114+
line: {
115+
width: {
116+
valType: 'number',
117+
min: 0,
118+
dflt: 2,
119+
editType: 'style',
120+
description: 'Sets the width (in px) of the arrow lines.'
121+
},
122+
dash: dash,
123+
editType: 'style'
124+
},
125+
editType: 'calc'
126+
},
127+
colorScaleAttrs('marker', {
128+
showScaleDflt: true,
129+
editTypeOverride: 'calc'
130+
})
131+
),
132+
selected: {
133+
marker: {
134+
color: {
135+
valType: 'color',
136+
editType: 'style',
137+
description: 'Sets the marker color of selected points.'
138+
},
139+
line: {
140+
width: {
141+
valType: 'number',
142+
min: 0,
143+
editType: 'style',
144+
description: 'Sets the line width of selected points.'
145+
},
146+
editType: 'style'
147+
},
148+
editType: 'style'
149+
},
150+
textfont: {
151+
color: {
152+
valType: 'color',
153+
editType: 'style',
154+
description: 'Sets the text font color of selected points, applied only when a selection exists.'
155+
},
156+
editType: 'style'
157+
},
158+
editType: 'style'
159+
},
160+
unselected: {
161+
marker: {
162+
color: {
163+
valType: 'color',
164+
editType: 'style',
165+
description: 'Sets the marker color of unselected points, applied only when a selection exists.'
166+
},
167+
line: {
168+
width: {
169+
valType: 'number',
170+
min: 0,
171+
editType: 'style',
172+
description: 'Sets the line width of unselected points, applied only when a selection exists.'
173+
},
174+
editType: 'style'
175+
},
176+
editType: 'style'
177+
},
178+
textfont: {
179+
color: {
180+
valType: 'color',
181+
editType: 'style',
182+
description: 'Sets the text font color of unselected points, applied only when a selection exists.'
183+
},
184+
editType: 'style'
185+
},
186+
editType: 'style'
187+
}
188+
};
189+
190+
module.exports = attrs;

0 commit comments

Comments
 (0)