Skip to content

Commit a9fe2a4

Browse files
committed
wip
1 parent 76b68f6 commit a9fe2a4

File tree

2 files changed

+105
-43
lines changed

2 files changed

+105
-43
lines changed

src/traces/scatterquiver/calc.js

Lines changed: 100 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,117 @@
11
'use strict';
22

33
var Lib = require('../../lib');
4+
var Axes = require('../../plots/cartesian/axes');
5+
var BADNUM = require('../../constants/numerical').BADNUM;
46

57
/**
68
* Main calculation function for scatterquiver trace
7-
* Creates calcdata with arrow path data for each vector
9+
* Creates calcdata with individual data points for each vector
810
*/
911
module.exports = function calc(gd, trace) {
10-
var x = trace.x;
11-
var y = trace.y;
12+
var fullLayout = gd._fullLayout;
13+
var xa = trace._xA = Axes.getFromId(gd, trace.xaxis || 'x', 'x');
14+
var ya = trace._yA = Axes.getFromId(gd, trace.yaxis || 'y', 'y');
15+
var origX = xa.makeCalcdata(trace, 'x');
16+
var origY = ya.makeCalcdata(trace, 'y');
17+
var x = origX;
18+
var y = origY;
1219
var u = trace.u;
1320
var v = trace.v;
1421
var scale = trace.scale;
1522
var arrowScale = trace.arrow_scale;
1623
var angle = trace.angle;
1724
var scaleRatio = trace.scaleratio;
1825

19-
// Create calcdata - one complete arrow per entry
20-
var calcdata = [];
21-
var len = x.length;
22-
23-
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);
26+
var serieslen = trace._length;
27+
var cd = new Array(serieslen);
28+
var ids = trace.ids;
29+
30+
// Set up axis expansion for proper scaling
31+
calcAxisExpansion(gd, trace, xa, ya, x, y);
32+
33+
for(var i = 0; i < serieslen; i++) {
34+
var cdi = cd[i] = {};
35+
var xValid = isNumeric(x[i]);
36+
var yValid = isNumeric(y[i]);
37+
var uValid = isNumeric(u[i]);
38+
var vValid = isNumeric(v[i]);
39+
40+
if(xValid && yValid && uValid && vValid) {
41+
cdi.x = x[i];
42+
cdi.y = y[i];
43+
cdi.u = u[i];
44+
cdi.v = v[i];
45+
46+
// For category aggregation, we need to set the right properties
47+
// The category aggregation looks for:
48+
// - catIndex = cdi.p (position) or cdi[axLetter] (x/y coordinate)
49+
// - value = cdi.s (size) or cdi.v (value) or opposite coordinate
50+
// We'll let the axis system handle the category index mapping
51+
cdi.s = Math.sqrt(u[i] * u[i] + v[i] * v[i]); // magnitude as size/value
52+
cdi.v = cdi.s; // also set v for value-based aggregation
53+
54+
// Calculate arrow components for rendering
55+
var dx = u[i] * scale * (scaleRatio || 1);
56+
var dy = v[i] * scale;
57+
var barbLen = Math.sqrt(dx * dx / (scaleRatio || 1) + dy * dy);
58+
var arrowLen = barbLen * arrowScale;
59+
var barbAng = Math.atan2(dy, dx / (scaleRatio || 1));
60+
61+
var ang1 = barbAng + angle;
62+
var ang2 = barbAng - angle;
63+
64+
var endX = x[i] + dx;
65+
var endY = y[i] + dy;
66+
67+
var point1X = endX - arrowLen * Math.cos(ang1) * (scaleRatio || 1);
68+
var point1Y = endY - arrowLen * Math.sin(ang1);
69+
var point2X = endX - arrowLen * Math.cos(ang2) * (scaleRatio || 1);
70+
var point2Y = endY - arrowLen * Math.sin(ang2);
71+
72+
// Store arrow path data for rendering
73+
cdi.arrowPath = [
74+
{ x: x[i], y: y[i] }, // Start point
75+
{ x: endX, y: endY }, // End of shaft
76+
{ x: point1X, y: point1Y }, // Arrow head point 1
77+
{ x: endX, y: endY }, // Back to end
78+
{ x: point2X, y: point2Y } // Arrow head point 2
79+
];
80+
} else {
81+
cdi.x = cdi.y = cdi.u = cdi.v = cdi.s = cdi.v = BADNUM;
82+
}
83+
84+
if(ids) {
85+
cdi.id = String(ids[i]);
86+
}
5287
}
5388

54-
return calcdata;
55-
};
89+
// Add original index to each point
90+
for(var i = 0; i < cd.length; i++) {
91+
cd[i].i = i;
92+
}
93+
94+
return cd;
95+
}
96+
97+
function isNumeric(val) {
98+
return typeof val === 'number' && !isNaN(val) && isFinite(val);
99+
}
100+
101+
function calcAxisExpansion(gd, trace, xa, ya, x, y) {
102+
var serieslen = trace._length;
103+
var ppad = 0.1; // padding for arrows
104+
105+
// Calculate padding based on arrow lengths
106+
for(var i = 0; i < serieslen; i++) {
107+
if(isNumeric(x[i]) && isNumeric(y[i]) && isNumeric(trace.u[i]) && isNumeric(trace.v[i])) {
108+
var dx = trace.u[i] * trace.scale * (trace.scaleratio || 1);
109+
var dy = trace.v[i] * trace.scale;
110+
var arrowLen = Math.sqrt(dx * dx + dy * dy);
111+
ppad = Math.max(ppad, arrowLen * 0.1);
112+
}
113+
}
114+
115+
xa.expand(x, {ppad: ppad});
116+
ya.expand(y, {ppad: ppad});
117+
}

src/traces/scatterquiver/plot.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
8484
// Update line segments
8585
lineSegments.each(function(d) {
8686
var path = d3.select(this);
87-
var segment = d;
87+
var point = d;
8888

89-
if(segment.length === 0) return;
89+
if(!point.arrowPath || point.arrowPath.length === 0) return;
9090

9191
// Convert data coordinates to pixel coordinates
92-
var pixelCoords = segment.map(function(point) {
92+
var pixelCoords = point.arrowPath.map(function(coord) {
9393
return {
94-
x: xa.c2p(point.x),
95-
y: ya.c2p(point.y)
94+
x: xa.c2p(coord.x),
95+
y: ya.c2p(coord.y)
9696
};
9797
});
9898

0 commit comments

Comments
 (0)