Skip to content

Commit 4320a67

Browse files
committed
Model quiver api closer to 3d cone trace
1 parent e02437e commit 4320a67

File tree

5 files changed

+106
-18
lines changed

5 files changed

+106
-18
lines changed

lib/quiver.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
'use strict';
22

33
module.exports = require('../src/traces/quiver');
4-
5-

src/traces/quiver/attributes.js

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,38 @@ var attrs = {
3232
anim: true,
3333
description: 'Sets the y components of the arrow vectors.'
3434
},
35-
scale: {
35+
sizemode: {
36+
valType: 'enumerated',
37+
values: ['scaled', 'absolute', 'raw'],
38+
editType: 'calc',
39+
dflt: 'scaled',
40+
description: [
41+
'Determines whether `sizeref` is set as a *scaled* (unitless) scalar',
42+
'(normalized by the max u/v norm in the vector field), as an *absolute*',
43+
'value (in the same units as the vector field), or *raw* to use the',
44+
'raw vector lengths.'
45+
].join(' ')
46+
},
47+
sizeref: {
3648
valType: 'number',
37-
dflt: 0.1,
3849
min: 0,
39-
max: 1,
4050
editType: 'calc',
41-
description: 'Scales size of the arrows (ideally to avoid overlap). Default = 0.1'
51+
description: [
52+
'Adjusts the arrow size scaling.',
53+
'The arrow length is determined by the vector norm multiplied by `sizeref`,',
54+
'optionally normalized when `sizemode` is *scaled*.'
55+
].join(' ')
56+
},
57+
anchor: {
58+
valType: 'enumerated',
59+
values: ['tip', 'tail', 'cm', 'center', 'middle'],
60+
dflt: 'tail',
61+
editType: 'calc',
62+
description: [
63+
'Sets the arrows\' anchor with respect to their (x,y) positions.',
64+
'Use *tail* to place (x,y) at the base, *tip* to place (x,y) at the head,',
65+
'or *cm*/*center*/*middle* to center the arrow on (x,y).'
66+
].join(' ')
4267
},
4368
arrow_scale: {
4469
valType: 'number',

src/traces/quiver/defaults.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3636
// Set basic properties
3737
traceOut.type = 'quiver';
3838

39+
// Sizing API similar to cone
40+
var sizemode = coerce('sizemode');
41+
coerce('sizeref', sizemode === 'raw' ? 1 : 0.5);
42+
coerce('anchor');
43+
3944
// Set default values using coerce
40-
coerce('scale', 0.1);
4145
coerce('arrow_scale', 0.3);
4246
coerce('angle', Math.PI / 9);
4347
coerce('scaleratio');

src/traces/quiver/plot.js

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
8181

8282
lineSegments.exit().remove();
8383

84+
// Precompute norms for sizing
85+
var uArr = trace.u || [];
86+
var vArr = trace.v || [];
87+
var maxNorm = 0;
88+
for (var ni = 0; ni < trace._length; ni++) {
89+
var uu = uArr[ni] || 0;
90+
var vv = vArr[ni] || 0;
91+
var nrm = Math.sqrt(uu * uu + vv * vv);
92+
if (nrm > maxNorm) maxNorm = nrm;
93+
}
94+
var sizemode = trace.sizemode || 'scaled';
95+
var sizeref = (trace.sizeref !== undefined) ? trace.sizeref : (sizemode === 'raw' ? 1 : 0.5);
96+
var anchor = trace.anchor || 'tail';
97+
8498
// Update line segments
8599
lineSegments.each(function(cdi) {
86100
var path = d3.select(this);
@@ -92,27 +106,52 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
92106
}
93107

94108
// Compute arrow in data space
95-
var scale = trace.scale || 1;
96109
var scaleRatio = trace.scaleratio || 1;
97110
var arrowScale = trace.arrow_scale || 0.2;
98111
var angle = trace.angle || Math.PI / 12; // small default
99112

100113
var u = (trace.u && trace.u[cdi.i]) || 0;
101114
var v = (trace.v && trace.v[cdi.i]) || 0;
102115

103-
var dx = u * scale * scaleRatio;
104-
var dy = v * scale;
116+
var norm = Math.sqrt(u * u + v * v);
117+
var unitx = norm ? (u / norm) : 0;
118+
var unity = norm ? (v / norm) : 0;
119+
var baseLen;
120+
if (sizemode === 'scaled') {
121+
var n = maxNorm ? (norm / maxNorm) : 0;
122+
baseLen = n * sizeref;
123+
} else {
124+
baseLen = norm * sizeref;
125+
}
126+
127+
var dxBase = unitx * baseLen;
128+
var dyBase = unity * baseLen;
129+
var dx = dxBase * scaleRatio;
130+
var dy = dyBase;
105131
var barbLen = Math.sqrt((dx * dx) / scaleRatio + dy * dy);
106132
var arrowLen = barbLen * arrowScale;
107133
var barbAng = Math.atan2(dy, dx / scaleRatio);
108134

109135
var ang1 = barbAng + angle;
110136
var ang2 = barbAng - angle;
111137

112-
var x0 = cdi.x;
113-
var y0 = cdi.y;
114-
var x1 = x0 + dx;
115-
var y1 = y0 + dy;
138+
var x0, y0, x1, y1;
139+
if (anchor === 'tip') {
140+
x1 = cdi.x;
141+
y1 = cdi.y;
142+
x0 = x1 - dx;
143+
y0 = y1 - dy;
144+
} else if (anchor === 'cm' || anchor === 'center' || anchor === 'middle') {
145+
x0 = cdi.x - dx / 2;
146+
y0 = cdi.y - dy / 2;
147+
x1 = cdi.x + dx / 2;
148+
y1 = cdi.y + dy / 2;
149+
} else { // tail
150+
x0 = cdi.x;
151+
y0 = cdi.y;
152+
x1 = x0 + dx;
153+
y1 = y0 + dy;
154+
}
116155

117156
var xh1 = x1 - arrowLen * Math.cos(ang1) * scaleRatio;
118157
var yh1 = y1 - arrowLen * Math.sin(ang1);

test/plot-schema.json

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79618,14 +79618,36 @@
7961879618
"min": 0,
7961979619
"valType": "number"
7962079620
},
79621-
"scale": {
79622-
"description": "Scales size of the arrows (ideally to avoid overlap). Default = 0.1",
79623-
"dflt": 0.1,
79621+
"sizemode": {
79622+
"description": "Determines whether `sizeref` is set as a *scaled* (unitless) scalar (normalized by the max u/v norm in the vector field), as an *absolute* value (in the same units as the vector field), or *raw* to use the raw vector lengths.",
79623+
"dflt": "scaled",
79624+
"editType": "calc",
79625+
"valType": "enumerated",
79626+
"values": [
79627+
"scaled",
79628+
"absolute",
79629+
"raw"
79630+
]
79631+
},
79632+
"sizeref": {
79633+
"description": "Adjusts the arrow size scaling. The arrow length is determined by the vector norm multiplied by `sizeref`, optionally normalized when `sizemode` is *scaled*.",
7962479634
"editType": "calc",
79625-
"max": 1,
7962679635
"min": 0,
7962779636
"valType": "number"
7962879637
},
79638+
"anchor": {
79639+
"description": "Sets the arrows' anchor with respect to their (x,y) positions. Use *tail* to place (x,y) at the base, *tip* to place (x,y) at the head, or *cm*/*center*/*middle* to center the arrow on (x,y).",
79640+
"dflt": "tail",
79641+
"editType": "calc",
79642+
"valType": "enumerated",
79643+
"values": [
79644+
"tip",
79645+
"tail",
79646+
"cm",
79647+
"center",
79648+
"middle"
79649+
]
79650+
},
7962979651
"scaleratio": {
7963079652
"description": "The ratio between the scale of the y-axis and the scale of the x-axis (scale_y / scale_x). Default = null, the scale ratio is not fixed.",
7963179653
"editType": "calc",

0 commit comments

Comments
 (0)