Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/index-strict.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Plotly.register([
require('../src/traces/scatterpolargl/strict'),
require('./barpolar'),
require('./scattersmith'),
require('./quiver'),

// components
require('./calendars'),
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Plotly.register([
require('./scatterpolargl'),
require('./barpolar'),
require('./scattersmith'),
require('./quiver'),

// components
require('./calendars'),
Expand Down
3 changes: 3 additions & 0 deletions lib/quiver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict';

module.exports = require('../src/traces/quiver');
258 changes: 258 additions & 0 deletions src/traces/quiver/attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
'use strict';

var baseAttrs = require('../../plots/attributes');
var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs;
var fontAttrs = require('../../plots/font_attributes');
var dash = require('../../components/drawing/attributes').dash;

var extendFlat = require('../../lib/extend').extendFlat;
var colorScaleAttrs = require('../../components/colorscale/attributes');

var attrs = {
x: {
valType: 'data_array',
editType: 'calc+clearAxisTypes',
anim: true,
description: 'Sets the x coordinates of the arrow locations.'
},
y: {
valType: 'data_array',
editType: 'calc+clearAxisTypes',
anim: true,
description: 'Sets the y coordinates of the arrow locations.'
},
u: {
valType: 'data_array',
editType: 'calc',
anim: true,
description: 'Sets the x components of the arrow vectors.'
},
v: {
valType: 'data_array',
editType: 'calc',
anim: true,
description: 'Sets the y components of the arrow vectors.'
},
// Optional scalar field used for colorscale mapping. If omitted, |(u,v)| is used.
c: {
valType: 'data_array',
editType: 'calc',
anim: true,
description: 'Sets the scalar values used to map colors via the colorscale. If not provided, the magnitude sqrt(u^2 + v^2) is used.'
},
sizemode: {
valType: 'enumerated',
values: ['scaled', 'absolute', 'raw'],
editType: 'calc',
dflt: 'scaled',
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.'
].join(' ')
},
sizeref: {
valType: 'number',
min: 0,
editType: 'calc',
description: [
'Adjusts the arrow size scaling.',
'The arrow length is determined by the vector norm multiplied by `sizeref`,',
'optionally normalized when `sizemode` is *scaled*.'
].join(' ')
},
anchor: {
valType: 'enumerated',
values: ['tip', 'tail', 'cm', 'center', 'middle'],
dflt: 'tail',
editType: 'calc',
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).'
].join(' ')
},
hoverdistance: {
valType: 'number',
min: -1,
dflt: 20,
editType: 'calc',
description: 'Maximum distance (in pixels) to look for nearby arrows on hover.'
},

// Arrowhead sizing, consistent with annotations API naming
arrowsize: {
valType: 'number',
min: 0.3,
dflt: 1,
editType: 'calc',
description: [
'Scales the size of the arrow head relative to a base size.',
'Higher values produce larger heads.'
].join(' ')
},
// Back-compat alias
arrow_scale: {
valType: 'number',
min: 0,
max: 1,
editType: 'calc',
description: 'Deprecated alias for `arrowsize`-based sizing. Prefer using `arrowsize`.'
},

// Line styling for arrows
line: {
color: {
valType: 'color',
dflt: '#000',
editType: 'style',
description: 'Sets the color of the arrow lines.'
},
width: {
valType: 'number',
min: 0,
dflt: 1,
editType: 'style',
description: 'Sets the width (in px) of the arrow lines.'
},
dash: dash,
shape: {
valType: 'enumerated',
values: ['linear', 'spline', 'hv', 'vh', 'hvh', 'vhv'],
dflt: 'linear',
editType: 'plot',
description: 'Determines the line shape.'
},
smoothing: {
valType: 'number',
min: 0,
max: 1.3,
dflt: 1,
editType: 'plot',
description: 'Has an effect only if `shape` is set to *spline*. Sets the amount of smoothing.'
},
simplify: {
valType: 'boolean',
dflt: true,
editType: 'plot',
description: 'Simplifies lines by removing nearly-overlapping points.'
},
editType: 'style'
},

// Alias consistent with annotations; maps to line.width
arrowwidth: {
valType: 'number',
min: 0.1,
editType: 'style',
description: 'Sets the width (in px) of the arrow line (alias of `line.width`).'
},

// Text and labels
text: {
valType: 'data_array',
editType: 'calc',
anim: true,
description: 'Sets text elements associated with each (x,y) pair.'
},
textposition: {
valType: 'enumerated',
values: [
'top left', 'top center', 'top right',
'middle left', 'middle center', 'middle right',
'bottom left', 'bottom center', 'bottom right'
],
dflt: 'middle center',
editType: 'calc',
description: 'Sets the positions of the `text` elements with respects to the (x,y) coordinates.'
},
// Text font
textfont: fontAttrs({
editType: 'calc',
colorEditType: 'style',
arrayOk: true,
description: 'Sets the text font.'
}),

// Selection and styling
selected: {
line: {
color: {
valType: 'color',
editType: 'style',
description: 'Sets the line color of selected points.'
},
width: {
valType: 'number',
min: 0,
editType: 'style',
description: 'Sets the line width of selected points.'
},
editType: 'style'
},
textfont: {
color: {
valType: 'color',
editType: 'style',
description: 'Sets the text font color of selected points, applied only when a selection exists.'
},
editType: 'style'
},
editType: 'style'
},
unselected: {
line: {
color: {
valType: 'color',
editType: 'style',
description: 'Sets the line color of unselected points.'
},
width: {
valType: 'number',
min: 0,
editType: 'style',
description: 'Sets the line width of unselected points.'
},
editType: 'style'
},
textfont: {
color: {
valType: 'color',
editType: 'style',
description: 'Sets the text font color of unselected points, applied only when a selection exists.'
},
editType: 'style'
},
editType: 'style'
}
};

// Extend with base attributes (includes hoverinfo, etc.)
extendFlat(attrs, baseAttrs);

// Colorscale attributes to color arrows by |(u,v)| magnitude
extendFlat(
attrs,
colorScaleAttrs('', {
colorAttr: 'u/v norm',
showScaleDflt: true,
editTypeOverride: 'calc'
})
);

// Add hoverinfo with proper flags for quiver
// We need to create a new object to avoid mutating the shared base attributes
attrs.hoverinfo = extendFlat({}, baseAttrs.hoverinfo, {
flags: ['x', 'y', 'u', 'v', 'text', 'name'],
dflt: 'all'
});

// Add hovertemplate
attrs.hovertemplate = extendFlat({}, hovertemplateAttrs({}, {
keys: ['x', 'y', 'u', 'v', 'text', 'name']
}));

module.exports = attrs;


79 changes: 79 additions & 0 deletions src/traces/quiver/calc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');
var isNumeric = require('fast-isnumeric');
var BADNUM = require('../../constants/numerical').BADNUM;
var scatterCalc = require('../scatter/calc');
var colorscaleCalc = require('../../components/colorscale/calc');

/**
* Main calculation function for quiver trace
* Creates calcdata with arrow path data for each vector
*/
module.exports = function calc(gd, trace) {
// Map x/y through axes so category/date values become numeric calcdata
var xa = trace._xA = Axes.getFromId(gd, trace.xaxis || 'x', 'x');
var ya = trace._yA = Axes.getFromId(gd, trace.yaxis || 'y', 'y');

var xVals = xa.makeCalcdata(trace, 'x');
var yVals = ya.makeCalcdata(trace, 'y');

// u/v are read in plot using the original trace arrays via cdi.i

var len = Math.min(xVals.length, yVals.length);
trace._length = len;
var cd = new Array(len);

var normMin = Infinity;
var normMax = -Infinity;
var cMin = Infinity;
var cMax = -Infinity;
var hasC = Array.isArray(trace.c);

for(var i = 0; i < len; i++) {
var cdi = cd[i] = { i: i };
var xValid = isNumeric(xVals[i]);
var yValid = isNumeric(yVals[i]);

if(xValid && yValid) {
cdi.x = xVals[i];
cdi.y = yVals[i];
} else {
cdi.x = BADNUM;
cdi.y = BADNUM;
}

// track ranges for colorscale
if(hasC) {
var ci = trace.c[i];
if(isNumeric(ci)) {
if(ci < cMin) cMin = ci;
if(ci > cMax) cMax = ci;
}
} else {
var ui = (trace.u && trace.u[i]) || 0;
var vi = (trace.v && trace.v[i]) || 0;
var n = Math.sqrt(ui * ui + vi * vi);
if(isFinite(n)) {
if(n < normMin) normMin = n;
if(n > normMax) normMax = n;
}
}
}

// Ensure axes are expanded and categories registered like scatter traces do
scatterCalc.calcAxisExpansion(gd, trace, xa, ya, xVals, yVals);

// Colorscale cmin/cmax computation: prefer provided c, else magnitude
var vals = hasC ? [cMin, cMax] : [normMin, normMax];
colorscaleCalc(gd, trace, {
vals: vals,
containerStr: '',
cLetter: 'c'
});

return cd;
};


Loading