Skip to content

Commit 02c37f5

Browse files
committed
Ensure no-gl-jasmine tests pass
1 parent 76b68f6 commit 02c37f5

File tree

4 files changed

+111
-83
lines changed

4 files changed

+111
-83
lines changed

src/traces/scatterquiver/calc.js

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,47 @@
11
'use strict';
22

33
var Lib = require('../../lib');
4+
var Axes = require('../../plots/cartesian/axes');
5+
var isNumeric = require('fast-isnumeric');
6+
var BADNUM = require('../../constants/numerical').BADNUM;
7+
var scatterCalc = require('../scatter/calc');
48

59
/**
610
* Main calculation function for scatterquiver trace
711
* Creates calcdata with arrow path data for each vector
812
*/
913
module.exports = function calc(gd, trace) {
10-
var x = trace.x;
11-
var y = trace.y;
12-
var u = trace.u;
13-
var v = trace.v;
14-
var scale = trace.scale;
15-
var arrowScale = trace.arrow_scale;
16-
var angle = trace.angle;
17-
var scaleRatio = trace.scaleratio;
18-
19-
// Create calcdata - one complete arrow per entry
20-
var calcdata = [];
21-
var len = x.length;
22-
14+
// Map x/y through axes so category/date values become numeric calcdata
15+
var xa = trace._xA = Axes.getFromId(gd, trace.xaxis || 'x', 'x');
16+
var ya = trace._yA = Axes.getFromId(gd, trace.yaxis || 'y', 'y');
17+
18+
var xVals = xa.makeCalcdata(trace, 'x');
19+
var yVals = ya.makeCalcdata(trace, 'y');
20+
21+
// u/v are read in plot using the original trace arrays via cdi.i
22+
23+
var len = Math.min(xVals.length, yVals.length);
24+
trace._length = len;
25+
var cd = new Array(len);
26+
2327
for(var i = 0; i < len; i++) {
24-
// Calculate arrow components
25-
var dx = u[i] * scale * (scaleRatio || 1);
26-
var dy = v[i] * scale;
27-
var barbLen = Math.sqrt(dx * dx / (scaleRatio || 1) + dy * dy);
28-
var arrowLen = barbLen * arrowScale;
29-
var barbAng = Math.atan2(dy, dx / (scaleRatio || 1));
30-
31-
var ang1 = barbAng + angle;
32-
var ang2 = barbAng - angle;
33-
34-
var endX = x[i] + dx;
35-
var endY = y[i] + dy;
36-
37-
var point1X = endX - arrowLen * Math.cos(ang1) * (scaleRatio || 1);
38-
var point1Y = endY - arrowLen * Math.sin(ang1);
39-
var point2X = endX - arrowLen * Math.cos(ang2) * (scaleRatio || 1);
40-
var point2Y = endY - arrowLen * Math.sin(ang2);
41-
42-
// Create complete arrow as one path: shaft + arrow head
43-
var arrowPath = [
44-
{ x: x[i], y: y[i], i: i }, // Start point
45-
{ x: endX, y: endY, i: i }, // End of shaft
46-
{ x: point1X, y: point1Y, i: i }, // Arrow head point 1
47-
{ x: endX, y: endY, i: i }, // Back to end
48-
{ x: point2X, y: point2Y, i: i } // Arrow head point 2
49-
];
50-
51-
calcdata.push(arrowPath);
28+
var cdi = cd[i] = { i: i };
29+
var xValid = isNumeric(xVals[i]);
30+
var yValid = isNumeric(yVals[i]);
31+
32+
if(xValid && yValid) {
33+
cdi.x = xVals[i];
34+
cdi.y = yVals[i];
35+
} else {
36+
cdi.x = BADNUM;
37+
cdi.y = BADNUM;
38+
}
39+
40+
// No additional props; keep minimal to avoid collisions with generic fields (e.g. `v`)
5241
}
5342

54-
return calcdata;
43+
// Ensure axes are expanded and categories registered like scatter traces do
44+
scatterCalc.calcAxisExpansion(gd, trace, xa, ya, xVals, yVals);
45+
46+
return cd;
5547
};

src/traces/scatterquiver/defaults.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,24 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
1717

1818
// Simple validation - check if we have the required arrays
1919
if(!x || !Array.isArray(x) || x.length === 0 ||
20-
!y || !Array.isArray(y) || y.length === 0 ||
21-
!u || !Array.isArray(u) || u.length === 0 ||
22-
!v || !Array.isArray(v) || v.length === 0) {
20+
!y || !Array.isArray(y) || y.length === 0) {
2321
traceOut.visible = false;
2422
return;
2523
}
2624

25+
// If u/v are missing, default to zeros so the trace participates in calc/category logic
26+
var len = Math.min(x.length, y.length);
27+
if(!Array.isArray(u) || u.length === 0) {
28+
traceOut.u = new Array(len);
29+
for(var i = 0; i < len; i++) traceOut.u[i] = 0;
30+
}
31+
if(!Array.isArray(v) || v.length === 0) {
32+
traceOut.v = new Array(len);
33+
for(var j = 0; j < len; j++) traceOut.v[j] = 0;
34+
}
35+
2736
// Set basic properties
2837
traceOut.type = 'scatterquiver';
29-
traceOut.visible = true;
3038

3139
// Set default values using coerce
3240
coerce('scale', 0.1);
@@ -64,5 +72,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
6472
coerce('unselected.textfont.color');
6573

6674
// Set the data length
67-
traceOut._length = x.length;
75+
traceOut._length = len;
6876
};

src/traces/scatterquiver/hover.js

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,47 +12,47 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
1212
var xpx = xa.c2p(xval);
1313
var ypx = ya.c2p(yval);
1414

15-
// Find the closest arrow to the hover point
15+
// Find the closest arrow base point to the hover point
1616
var minDistance = Infinity;
1717
var closestPoint = null;
1818
var closestIndex = -1;
1919

20-
// Check each arrow segment
20+
// Each cd[i] is a calcdata point object with x/y
2121
for(var i = 0; i < cd.length; i++) {
22-
var segment = cd[i];
23-
if(segment.length < 2) continue;
22+
var cdi = cd[i];
23+
if(cdi.x === undefined || cdi.y === undefined) continue;
24+
25+
var px = xa.c2p(cdi.x);
26+
var py = ya.c2p(cdi.y);
27+
28+
var distance = Math.sqrt((xpx - px) * (xpx - px) + (ypx - py) * (ypx - py));
2429

25-
// Calculate distance to the start point of the arrow
26-
var x1 = xa.c2p(segment[0].x);
27-
var y1 = ya.c2p(segment[0].y);
28-
29-
var distance = Math.sqrt((xpx - x1) * (xpx - x1) + (ypx - y1) * (ypx - y1));
30-
3130
if(distance < minDistance) {
3231
minDistance = distance;
33-
closestPoint = segment[0]; // Use the start point for hover data
32+
closestPoint = cdi;
3433
closestIndex = i;
3534
}
3635
}
3736

38-
if(!closestPoint || minDistance > (trace.hoverdistance || 20)) return;
37+
var maxHoverDist = pointData.distance === Infinity ? Infinity : (trace.hoverdistance || 20);
38+
if(!closestPoint || minDistance > maxHoverDist) return;
3939

4040
// Create hover point data with proper label values and spikeline support
4141
var hoverPoint = {
4242
x: closestPoint.x,
4343
y: closestPoint.y,
44-
u: trace.u[closestIndex],
45-
v: trace.v[closestIndex],
46-
text: trace.text ? trace.text[closestIndex] : '',
44+
u: trace.u ? trace.u[closestIndex] : undefined,
45+
v: trace.v ? trace.v[closestIndex] : undefined,
46+
text: Array.isArray(trace.text) ? trace.text[closestIndex] : trace.text,
4747
name: trace.name || '',
4848
trace: trace,
4949
index: closestIndex,
50-
// Set label values for proper hover formatting
50+
// Label values for formatting
5151
xLabelVal: closestPoint.x,
5252
yLabelVal: closestPoint.y,
53-
uLabelVal: trace.u[closestIndex],
54-
vLabelVal: trace.v[closestIndex],
55-
// Add spikeline support
53+
uLabelVal: trace.u ? trace.u[closestIndex] : undefined,
54+
vLabelVal: trace.v ? trace.v[closestIndex] : undefined,
55+
// Spikeline support
5656
xa: pointData.xa,
5757
ya: pointData.ya,
5858
x0: closestPoint.x,

src/traces/scatterquiver/plot.js

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
7171

7272
Drawing.setClipUrl(lines, plotinfo.layerClipId, gd);
7373

74-
// Create line segments for each arrow
74+
// Create one path per data point (arrow)
7575
var lineSegments = lines.selectAll('path.js-line')
7676
.data(cdscatter);
7777

@@ -82,26 +82,54 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
8282
lineSegments.exit().remove();
8383

8484
// Update line segments
85-
lineSegments.each(function(d) {
85+
lineSegments.each(function(cdi) {
8686
var path = d3.select(this);
87-
var segment = d;
88-
89-
if(segment.length === 0) return;
90-
91-
// Convert data coordinates to pixel coordinates
92-
var pixelCoords = segment.map(function(point) {
93-
return {
94-
x: xa.c2p(point.x),
95-
y: ya.c2p(point.y)
96-
};
97-
});
9887

99-
// Create SVG path from pixel coordinates
100-
var pathData = 'M' + pixelCoords[0].x + ',' + pixelCoords[0].y;
101-
for(var i = 1; i < pixelCoords.length; i++) {
102-
pathData += 'L' + pixelCoords[i].x + ',' + pixelCoords[i].y;
88+
// Skip invalid points
89+
if(cdi.x === undefined || cdi.y === undefined) {
90+
path.attr('d', null);
91+
return;
10392
}
10493

94+
// Compute arrow in data space
95+
var scale = trace.scale || 1;
96+
var scaleRatio = trace.scaleratio || 1;
97+
var arrowScale = trace.arrow_scale || 0.2;
98+
var angle = trace.angle || Math.PI / 12; // small default
99+
100+
var u = (trace.u && trace.u[cdi.i]) || 0;
101+
var v = (trace.v && trace.v[cdi.i]) || 0;
102+
103+
var dx = u * scale * scaleRatio;
104+
var dy = v * scale;
105+
var barbLen = Math.sqrt((dx * dx) / scaleRatio + dy * dy);
106+
var arrowLen = barbLen * arrowScale;
107+
var barbAng = Math.atan2(dy, dx / scaleRatio);
108+
109+
var ang1 = barbAng + angle;
110+
var ang2 = barbAng - angle;
111+
112+
var x0 = cdi.x;
113+
var y0 = cdi.y;
114+
var x1 = x0 + dx;
115+
var y1 = y0 + dy;
116+
117+
var xh1 = x1 - arrowLen * Math.cos(ang1) * scaleRatio;
118+
var yh1 = y1 - arrowLen * Math.sin(ang1);
119+
var xh2 = x1 - arrowLen * Math.cos(ang2) * scaleRatio;
120+
var yh2 = y1 - arrowLen * Math.sin(ang2);
121+
122+
// Convert to pixels
123+
var p0x = xa.c2p(x0);
124+
var p0y = ya.c2p(y0);
125+
var p1x = xa.c2p(x1);
126+
var p1y = ya.c2p(y1);
127+
var ph1x = xa.c2p(xh1);
128+
var ph1y = ya.c2p(yh1);
129+
var ph2x = xa.c2p(xh2);
130+
var ph2y = ya.c2p(yh2);
131+
132+
var pathData = 'M' + p0x + ',' + p0y + 'L' + p1x + ',' + p1y + 'L' + ph1x + ',' + ph1y + 'L' + p1x + ',' + p1y + 'L' + ph2x + ',' + ph2y;
105133
path.attr('d', pathData);
106134
});
107135

0 commit comments

Comments
 (0)