Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
7c74abe
Add scatterquiver trace type
degzhaus Oct 21, 2025
100bd88
Ensure no-gl-jasmine tests pass
degzhaus Oct 22, 2025
3f24d4a
Name plot type quiver not scatterquiver
degzhaus Nov 2, 2025
ec6a266
Model quiver api closer to 3d cone trace
degzhaus Nov 3, 2025
68a7608
Match arrowhead attributes for annotations
degzhaus Nov 11, 2025
1d8d74e
Derive scaling from axes
degzhaus Nov 11, 2025
3dd208d
Remove angle attribute
degzhaus Nov 11, 2025
ec5f850
Include colorscale attributes in quiver
degzhaus Nov 11, 2025
5534667
Add support for coloring by an independent scalar array
degzhaus Nov 11, 2025
5302654
Run npm run schema
degzhaus Nov 11, 2025
9d8fa14
Use isArrayOrTypedArray
degzhaus Nov 19, 2025
7c2055f
Add quiver jasmine and image tests
degzhaus Nov 17, 2025
00638de
Generate baseline images for quiver tests
degzhaus Nov 17, 2025
df33638
Run npm run schema
degzhaus Nov 30, 2025
a1c40a6
Improve code readability
degzhaus Dec 18, 2025
4d9f656
Reuse scatter selectPoints for quiver instead of duplicating selectio…
degzhaus Feb 16, 2026
452ba9d
Use hasColorscale() to conditionally enable colorscale instead of har…
degzhaus Feb 16, 2026
3c42def
Reuse scatter handleXYDefaults() for quiver x/y coercion, validation,…
degzhaus Feb 16, 2026
36cab21
Move colorscale attrs under marker and replace custom c attribute wit…
degzhaus Feb 16, 2026
b8e2aa8
Fix axis autorange to include arrow tips, not just base positions; re…
degzhaus Feb 16, 2026
13bb96a
Add legend line icon for quiver traces by including quiver in getStyl…
degzhaus Feb 16, 2026
d4ad7ba
Add marker_colorbar config to quiver module so colorbars render corre…
degzhaus Feb 16, 2026
7556092
Implement visual text label rendering for quiver using shared Drawing…
degzhaus Feb 16, 2026
fa078c2
Implement per-arrow selected/unselected styling with opacity dimming …
degzhaus Feb 16, 2026
9cb41db
Run npm run schema
degzhaus Feb 16, 2026
7f634ed
Fix colorbar crash by ensuring traceOut.marker exists before colorsca…
degzhaus Feb 16, 2026
37c2a8d
Regenerate image baselines
degzhaus Mar 9, 2026
6d613d7
Nest line options under marker.line
degzhaus Mar 9, 2026
37a6cd2
Move arrowheadsize under marker
degzhaus Mar 9, 2026
c40a5e0
Run npm run schema
degzhaus Mar 9, 2026
fcb5701
Regenerate quiver custom colorscale
degzhaus Mar 9, 2026
73f0338
Import scatter attributes for x, y coercion
degzhaus Mar 9, 2026
e46104b
Run npm run schema
degzhaus Mar 9, 2026
48e4e7e
Passing tests
degzhaus Apr 6, 2026
a2799e2
Adjust axis ranges automatically to include full length of arrows
degzhaus Jun 24, 2026
3344864
Honor colorscale
degzhaus Jun 24, 2026
94dc00d
Use center, Remove redundant anchor value options
degzhaus Jun 24, 2026
2e214c7
Idiomatic extendFlat usage
degzhaus Jun 24, 2026
f3789e6
Fallback to arrow color with opacity
degzhaus Jun 24, 2026
dd71b94
Guard against infinite values
degzhaus Jun 24, 2026
eba15eb
Remove unused imports
degzhaus Jun 24, 2026
5d3e198
Merge remote-tracking branch 'origin/v4.0' into degzhaus/add_quiver
camdecoster Jun 24, 2026
813ab71
Update schema and types
camdecoster Jun 24, 2026
0a76daf
Regenerate image baselines
degzhaus Jun 24, 2026
c694a02
Add quiver mock to svgMockList
degzhaus Jun 24, 2026
601ca45
Add draftlog/7710_add.md for quiver trace type
degzhaus Jun 24, 2026
86030c3
Merge remote-tracking branch 'origin/v4.0' into degzhaus/add_quiver
camdecoster Jun 25, 2026
d92fd9b
Update types
camdecoster Jun 25, 2026
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 draftlogs/7710_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add `quiver` trace type to visualize vector fields using arrows [[#7710](https://github.com/plotly/plotly.js/pull/7710)]
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');
2 changes: 1 addition & 1 deletion src/components/legend/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ function getGradientDirection(reversescale, isRadial) {
function getStyleGuide(d) {
var trace = d[0].trace;
var contours = trace.contours;
var showLine = subTypes.hasLines(trace);
var showLine = subTypes.hasLines(trace) || (trace.visible && trace.type === 'quiver');
var showMarker = subTypes.hasMarkers(trace);

var showFill = trace.visible && trace.fill && trace.fill !== 'none';
Expand Down
212 changes: 212 additions & 0 deletions src/traces/quiver/attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
'use strict';

var baseAttrs = require('../../plots/attributes');
var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs;
var fontAttrs = require('../../plots/font_attributes');
var axisHoverFormat = require('../../plots/cartesian/axis_format_attributes').axisHoverFormat;
var extendFlat = require('../../lib/extend').extendFlat;
var colorScaleAttrs = require('../../components/colorscale/attributes');
var dash = require('../../components/drawing/attributes').dash;
var annotationAttrs = require('../../components/annotations/attributes');
var scatterAttrs = require('../scatter/attributes');

var attrs = {
x: {
valType: 'data_array',
editType: 'calc+clearAxisTypes',
anim: true,
description: 'Sets the x coordinates of the arrow locations.'
},
x0: scatterAttrs.x0,
dx: scatterAttrs.dx,
y: {
valType: 'data_array',
editType: 'calc+clearAxisTypes',
anim: true,
description: 'Sets the y coordinates of the arrow locations.'
},
y0: scatterAttrs.y0,
dy: scatterAttrs.dy,
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.'
},
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', 'center'],
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 *center* 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.'
},

xhoverformat: axisHoverFormat('x'),
yhoverformat: axisHoverFormat('y'),
uhoverformat: axisHoverFormat('u', 'noDate'),
vhoverformat: axisHoverFormat('v', 'noDate'),

// 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.'
}),

// Marker: color, colorscale, arrowhead sizing, and line styling for arrows
marker: extendFlat(
{
arrowsize: extendFlat({}, annotationAttrs.arrowsize, {
editType: 'calc',
description: [
'Sets the size of the arrow head relative to `marker.line.width`.',
'A value of 1 (default) gives a head about 3x as wide as the line.'
].join(' ')
}),
line: {
width: {
valType: 'number',
min: 0,
dflt: 1,
editType: 'style',
description: 'Sets the width (in px) of the arrow lines.'
},
dash: dash,
editType: 'style'
},
editType: 'calc'
},
colorScaleAttrs('marker', {
showScaleDflt: true,
editTypeOverride: 'calc'
})
),

// 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.), keeping the
// quiver-specific attributes layered on top of the shared base attributes.
attrs = extendFlat({}, baseAttrs, attrs);

// 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;
Loading
Loading