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 draftlogs/7945_change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Rename `quiver` trace attributes for clarity and consistency: `sizeref` -> `lengthfactor`, `sizemode` -> `lengthmode`, `anglemode` -> `uvref` [[#7945](https://github.com/plotly/plotly.js/issues/7945)]
39 changes: 20 additions & 19 deletions src/traces/quiver/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,62 +14,63 @@ var attrs = {
valType: 'data_array',
editType: 'calc+clearAxisTypes',
anim: true,
description: 'Sets the x coordinates of the arrow locations.'
description: 'Sets the x coordinates of the vector 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.'
description: 'Sets the y coordinates of the vector 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.'
description: 'Sets the x components of the vector arrows.'
},
v: {
valType: 'data_array',
editType: 'calc',
anim: true,
description: 'Sets the y components of the arrow vectors.'
description: 'Sets the y components of the vector arrows.'
},
anglemode: {
uvref: {
valType: 'enumerated',
values: ['paper', 'data'],
dflt: 'axis',
dflt: 'data',
editType: 'calc',
description: [
'Sets the mode used to determine the angle of the arrow vectors.',
'Determines how the u/v vector components are interpreted.',
'If *paper*, u/v are interpreted in pixel coordinates and the rendered vector angle',
'does not change regardless of the axes scales.',
'does not change regardless of the axis scales.',
'If *data*, u/v are interpreted in data coordinates and the rendered vector angle',
'may change, e.g. if zooming in along a single axis'
].join(' ')
},
sizemode: {
lengthmode: {
valType: 'enumerated',
values: ['scaled', 'raw'],
editType: 'calc',
dflt: 'scaled',
description: [
'Determines whether arrows are drawn according to their raw lengths,',
'or scaled based on the maximum vector length and point density. Note: When `anglemode` is *data*',
'arrows are alwyas scaled and `sizemode` *raw* is ignored.',
'Determines whether vector arrows are drawn according to their raw lengths,',
'or scaled based on the maximum vector length and point density. Note: When `uvref` is *paper*',
'vectors are always scaled and `lengthmode` *raw* is ignored.'
].join(' ')
},
sizeref: {
lengthfactor: {
valType: 'number',
min: 0,
editType: 'calc',
dflt: 1,
description: [
'Adjusts the arrow size scaling. The arrow length is determined by the vector norm multiplied by `sizeref`,',
'optionally normalized when `sizemode` is *scaled* (`sizeref` is applied after scaling).'
'Adjusts the drawn length of the vector arrows. The arrow length is determined by',
'the values of u and v, then optionally rescaled when `lengthmode` is *scaled*,',
'then multiplied by `lengthfactor`.',
].join(' ')
},
anchor: {
Expand All @@ -78,9 +79,9 @@ var attrs = {
dflt: 'tail',
editType: 'calc',
description: [
'Sets the arrows\' anchor with respect to their (x,y) positions.',
'Sets the vector 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).'
'or *center* to center the vector arrow on (x,y).'
].join(' ')
},
xhoverformat: axisHoverFormat('x'),
Expand All @@ -107,7 +108,7 @@ var attrs = {
arrowsize: extendFlat({}, annotationAttrs.arrowsize, {
editType: 'calc',
description: [
'Sets the size of the arrow head relative to `marker.line.width`.',
'Sets the size of the vector arrowhead relative to `marker.line.width`.',
'A value of 1 (default) gives a head about 3x as wide as the line.'
].join(' ')
}),
Expand All @@ -117,7 +118,7 @@ var attrs = {
min: 0,
dflt: 2,
editType: 'style',
description: 'Sets the width (in px) of the arrow lines.'
description: 'Sets the width (in px) of the vector arrow lines.'
},
dash: dash,
editType: 'style'
Expand Down
34 changes: 17 additions & 17 deletions src/traces/quiver/calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ module.exports = function calc(gd, trace) {
const uArr = trace.u || [];
const vArr = trace.v || [];

const anglemode = trace.anglemode;
const sizemode = trace.sizemode;
const uvref = trace.uvref;
const lengthmode = trace.lengthmode;
const anchor = trace.anchor;
const isTip = anchor === 'tip';
const isCenter = anchor === 'center';
Expand All @@ -54,7 +54,7 @@ module.exports = function calc(gd, trace) {
var nValid = 0;

// First pass: build calcdata, and keep track of the maximum and minimum vector norm in the trace,
// to be used for sizemode 'scaled' (max norm only) and for magnitude-based colorscale range
// to be used for lengthmode 'scaled' (max norm only) and for magnitude-based colorscale range
for(var i = 0; i < len; i++) {
var cdi = cd[i] = { i: i };
var xValid = isNumeric(xVals[i]);
Expand Down Expand Up @@ -109,19 +109,19 @@ module.exports = function calc(gd, trace) {
// Store maxNorm for use by plot step
trace._maxNorm = normMax;

if (sizemode === 'scaled' || anglemode === 'paper') {
// Ignore sizemode 'raw' if anglemode is set to 'paper': always scale
if (lengthmode === 'scaled' || uvref === 'paper') {
// Ignore lengthmode 'raw' if uvref is set to 'paper': always scale

// Compute point density of the entire trace: Area of bounding box
// divided by number of points. This is used to scale arrows in
// 'scaled' sizemode.
// 'scaled' lengthmode.
// TODO: How to handle the case where there is just one point in a trace,
// or all points have the same x or y value? This will give a boxArea of 0.
// For now I'm going to just normalize to a vector of unit length (1) in that case,
// but that's not a great solution
const boxArea = (xMax - xMin) * (yMax - yMin);
const pointDensity = boxArea / len;
// Now, compute the scale factor for scaled size mode
// Now, compute the scale factor for scaled length mode
// The scale factor should be such that
// _maxNorm * _scaleFactor = Math.sqrt(_pointDensity)
// Therefore: _scaleFactor = Math.sqrt(_pointDensity) / _maxNorm
Expand All @@ -130,16 +130,16 @@ module.exports = function calc(gd, trace) {
} else {
trace._scaleFactor = Math.sqrt(pointDensity) / trace._maxNorm;
}
// Note: If anglemode === 'paper', this scale factor must be
// Note: If uvref === 'paper', this scale factor must be
// multiplied by Math.sqrt(xa._m * ya._m), but we can't do that quite yet
// since the axis scales are not fully determined. Do it in plot step instead.
} else { // sizemode === 'raw'
// For raw sizemode, scale factor is always 1
} else { // lengthmode === 'raw'
// For raw lengthmode, scale factor is always 1
trace._scaleFactor = 1;
}

// Multiply scale factor by sizeref
trace._scaleFactor *= trace.sizeref;
// Multiply computed scale factor by lengthfactor attr
trace._scaleFactor *= trace.lengthfactor;

// Now we need to compute the arrow geometry for axis autorange
const xTipPositions = new Array(len);
Expand All @@ -148,7 +148,7 @@ module.exports = function calc(gd, trace) {
const yTailPositions = new Array(len);
var arrowLenX, arrowLenY;
// Compute the x- and y-positions of the tip of each arrow,
// assuming anglemode === 'data' (i.e. u/v are in data coordinates)
// assuming uvref === 'data' (i.e. u/v are in data coordinates)
for(var i = 0; i < len; i++) {
var cdi = cd[i];
arrowLenX = cdi._u * trace._scaleFactor;
Expand All @@ -171,12 +171,12 @@ module.exports = function calc(gd, trace) {
}
}

if (anglemode === 'data') {
// If anglemode is 'data', we can use the arrow tip positions directly to expand the axes ranges
if (uvref === 'data') {
// If uvref is 'data', we can use the arrow tip positions directly to expand the axes ranges
trace._extremes[xa._id] = Axes.findExtremes(xa, xTipPositions.concat(xTailPositions), {padded: true});
trace._extremes[ya._id] = Axes.findExtremes(ya, yTipPositions.concat(yTailPositions), {padded: true});
} else { // anglemode === 'paper'
// TODO: For now, just do the same thing as for anglemode === 'data', but this is not correct.
} else { // uvref === 'paper'
// TODO: For now, just do the same thing as for uvref === 'data', but this is not correct.
// We actually need more sophisticated logic here, since this will give a bad result
// if the data aspect ratio is very different from the plot aspect ratio.
trace._extremes[xa._id] = Axes.findExtremes(xa, xTipPositions.concat(xTailPositions), {padded: true});
Expand Down
6 changes: 3 additions & 3 deletions src/traces/quiver/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
for(var j = 0; j < len; j++) traceOut.v[j] = 0;
}

coerce('anglemode');
coerce('uvref');

// Sizing API inspired by cone, but not identical
coerce('sizemode');
coerce('sizeref');
coerce('lengthmode');
coerce('lengthfactor');
coerce('anchor');

// Arrow styling
Expand Down
16 changes: 7 additions & 9 deletions src/traces/quiver/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,11 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition

// Use maxNorm precomputed in calc
const maxNorm = trace._maxNorm || 0;
const anglemode = trace.anglemode;
const sizemode = trace.sizemode;
const sizeref = trace.sizeref;
const uvref = trace.uvref;
const anchor = trace.anchor;

// Adjust scale factor if anglemode is 'paper'
const scaleFactor = (anglemode === 'paper') ? trace._scaleFactor * Math.sqrt(Math.abs(xa._m * ya._m)) : trace._scaleFactor;
// Adjust scale factor if uvref is 'paper'
const scaleFactor = (uvref === 'paper') ? trace._scaleFactor * Math.sqrt(Math.abs(xa._m * ya._m)) : trace._scaleFactor;
const markerArrowsize = trace.marker.arrowsize;

// Update line segments
Expand All @@ -123,12 +121,12 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
}

// Compute pixel location of vector tip, *relative to* vector base (before scaling).
// If anglemode is 'paper', then u/v are interpreted in pixel coordinates, so we can use them directly.
// If anglemode is 'data', then u/v are interpreted in data coordinates, so we need to convert them to pixel coordinates.
// If uvref is 'paper', then u/v are interpreted in pixel coordinates, so we can use them directly.
// If uvref is 'data', then u/v are interpreted in data coordinates, so we need to convert them to pixel coordinates.
// TODO: This probably doesn't work for log axes, but let's ignore log axes for now
// since I'm not sure they make sense for quiver plots anyway
const pu = ((anglemode === 'paper') ? cdi._u * Math.sign(xa._m) : d3.round(xa._m * cdi._u)) * scaleFactor;
const pv = ((anglemode === 'paper') ? cdi._v * Math.sign(ya._m) : d3.round(ya._m * cdi._v)) * scaleFactor;
const pu = ((uvref === 'paper') ? cdi._u * Math.sign(xa._m) : d3.round(xa._m * cdi._u)) * scaleFactor;
const pv = ((uvref === 'paper') ? cdi._v * Math.sign(ya._m) : d3.round(ya._m * cdi._v)) * scaleFactor;

// Compute arrow in data space
// Check whether arrowsize was set explicitly in the input trace
Expand Down
46 changes: 23 additions & 23 deletions src/types/generated/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6680,15 +6680,10 @@ export interface PieData {

export interface QuiverData {
/**
* 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).
* Sets the vector 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 vector arrow on (x,y).
* @default 'tail'
*/
anchor?: 'tip' | 'tail' | 'center';
/**
* Sets the mode used to determine the angle of the arrow vectors. If *paper*, u/v are interpreted in pixel coordinates and the rendered vector angle does not change regardless of the axes scales. If *data*, u/v are interpreted in data coordinates and the rendered vector angle may change, e.g. if zooming in along a single axis
* @default 'axis'
*/
anglemode?: 'paper' | 'data';
/** Assigns extra data each datum. This may be useful when listening to hover, click and selection events. Note that, *scatter* traces also appends customdata items in the markers DOM elements */
customdata?: Datum[] | Datum[][] | TypedArray;
/**
Expand Down Expand Up @@ -6729,9 +6724,20 @@ export interface QuiverData {
* Minimum: 0
*/
legendwidth?: number;
/**
* Adjusts the drawn length of the vector arrows. The arrow length is determined by the values of u and v, then optionally rescaled when `lengthmode` is *scaled*, then multiplied by `lengthfactor`.
* @default 1
* Minimum: 0
*/
lengthfactor?: number;
/**
* Determines whether vector arrows are drawn according to their raw lengths, or scaled based on the maximum vector length and point density. Note: When `uvref` is *paper* vectors are always scaled and `lengthmode` *raw* is ignored.
* @default 'scaled'
*/
lengthmode?: 'scaled' | 'raw';
marker?: {
/**
* 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.
* Sets the size of the vector arrowhead relative to `marker.line.width`. A value of 1 (default) gives a head about 3x as wide as the line.
* @default 1
* Minimum: 0.3
*/
Expand Down Expand Up @@ -6775,7 +6781,7 @@ export interface QuiverData {
*/
dash?: Dash;
/**
* Sets the width (in px) of the arrow lines.
* Sets the width (in px) of the vector arrow lines.
* @default 2
* Minimum: 0
*/
Expand Down Expand Up @@ -6817,17 +6823,6 @@ export interface QuiverData {
* @default true
*/
showlegend?: boolean;
/**
* Determines whether arrows are drawn according to their raw lengths, or scaled based on the maximum vector length and point density. Note: When `anglemode` is *data* arrows are alwyas scaled and `sizemode` *raw* is ignored.
* @default 'scaled'
*/
sizemode?: 'scaled' | 'raw';
/**
* Adjusts the arrow size scaling. The arrow length is determined by the vector norm multiplied by `sizeref`, optionally normalized when `sizemode` is *scaled* (`sizeref` is applied after scaling).
* @default 1
* Minimum: 0
*/
sizeref?: number;
/** Sets text elements associated with each (x,y) pair. If a single string, the same string appears over all the data points. If an array of string, the items are mapped in order to the this trace's (x,y) coordinates. If trace `hoverinfo` contains a *text* flag and *hovertext* is not set, these elements will be seen in the hover labels. */
text?: string | string[];
/** Sets the text font. */
Expand All @@ -6838,7 +6833,7 @@ export interface QuiverData {
*/
textposition?: 'top left' | 'top center' | 'top right' | 'middle left' | 'middle center' | 'middle right' | 'bottom left' | 'bottom center' | 'bottom right' | ('top left' | 'top center' | 'top right' | 'middle left' | 'middle center' | 'middle right' | 'bottom left' | 'bottom center' | 'bottom right')[];
type?: 'quiver';
/** Sets the x components of the arrow vectors. */
/** Sets the x components of the vector arrows. */
u?: Datum[] | Datum[][] | TypedArray;
/** Sets the hover text formatting rule for `u` using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-format/tree/v1.4.5#d3-format.By default the values are formatted using generic number format. */
uhoverformat?: string;
Expand All @@ -6854,7 +6849,12 @@ export interface QuiverData {
};
textfont?: Font;
};
/** Sets the y components of the arrow vectors. */
/**
* Determines how the u/v vector components are interpreted. If *paper*, u/v are interpreted in pixel coordinates and the rendered vector angle does not change regardless of the axis scales. If *data*, u/v are interpreted in data coordinates and the rendered vector angle may change, e.g. if zooming in along a single axis
* @default 'data'
*/
uvref?: 'paper' | 'data';
/** Sets the y components of the vector arrows. */
v?: Datum[] | Datum[][] | TypedArray;
/** Sets the hover text formatting rule for `v` using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-format/tree/v1.4.5#d3-format.By default the values are formatted using generic number format. */
vhoverformat?: string;
Expand All @@ -6863,7 +6863,7 @@ export interface QuiverData {
* @default true
*/
visible?: true | false | 'legendonly';
/** Sets the x coordinates of the arrow locations. */
/** Sets the x coordinates of the vector arrow locations. */
x?: Datum[] | Datum[][] | TypedArray;
/**
* Alternate to `x`. Builds a linear space of x coordinates. Use with `dx` where `x0` is the starting coordinate and `dx` the step.
Expand All @@ -6877,7 +6877,7 @@ export interface QuiverData {
xaxis?: string;
/** Sets the hover text formatting rule for `x` using d3 formatting mini-languages which are very similar to those in Python. For numbers, see: https://github.com/d3/d3-format/tree/v1.4.5#d3-format. And for dates see: https://github.com/d3/d3-time-format/tree/v2.2.3#locale_format. We add two items to d3's date formatter: *%h* for half of the year as a decimal number as well as *%{n}f* for fractional seconds with n digits. For example, *2016-10-13 09:15:23.456* with tickformat *%H~%M~%S.%2f* would display *09~15~23.46*By default the values are formatted using `xaxis.hoverformat`. */
xhoverformat?: string;
/** Sets the y coordinates of the arrow locations. */
/** Sets the y coordinates of the vector arrow locations. */
y?: Datum[] | Datum[][] | TypedArray;
/**
* Alternate to `y`. Builds a linear space of y coordinates. Use with `dy` where `y0` is the starting coordinate and `dy` the step.
Expand Down
Binary file added test/image/baselines/quiver_lengthmode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed test/image/baselines/quiver_sizemode.png
Binary file not shown.
6 changes: 3 additions & 3 deletions test/image/mocks/quiver_anchor.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"y": [0, 0, 0, 1, 1, 1, 2, 2, 2],
"u": [1, 1, 1, 1, 1, 1, 1, 1, 1],
"v": [1, 1, 1, 1, 1, 1, 1, 1, 1],
"sizemode": "scaled",
"lengthmode": "scaled",
"anchor": "tail",
"marker": {
"color": "#4466ee",
Expand All @@ -23,7 +23,7 @@
"y": [0, 0, 0, 1, 1, 1, 2, 2, 2],
"u": [1, 1, 1, 1, 1, 1, 1, 1, 1],
"v": [1, 1, 1, 1, 1, 1, 1, 1, 1],
"sizemode": "scaled",
"lengthmode": "scaled",
"anchor": "tip",
"marker": {
"color": "#ee3344",
Expand All @@ -39,7 +39,7 @@
"y": [0, 0, 0, 1, 1, 1, 2, 2, 2],
"u": [1, 1, 1, 1, 1, 1, 1, 1, 1],
"v": [1, 1, 1, 1, 1, 1, 1, 1, 1],
"sizemode": "scaled",
"lengthmode": "scaled",
"anchor": "center",
"marker": {
"color": "#8811cc",
Expand Down
Loading
Loading