Skip to content

Commit 93b8c97

Browse files
authored
Merge pull request #8069 from Yahiro025/fix/quiver-discrete-marker-color-8026
fix(quiver): honor discrete marker.color arrays
2 parents 8ffa227 + 622f234 commit 93b8c97

4 files changed

Lines changed: 88 additions & 10 deletions

File tree

‎draftlogs/8069_fix.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fix quiver arrow colors for a discrete `marker.color` array [[#8069](https://github.com/plotly/plotly.js/pull/8069)], with thanks to @Yahiro025 for the contribution!

‎src/traces/quiver/plot.js‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var d3 = require('@plotly/d3');
44

55
var Lib = require('../../lib');
66
var Drawing = require('../../components/drawing');
7-
var colorscaleStroke = require('./style').colorscaleStroke;
7+
const { strokeArrowColors } = require('./style');
88

99
// Fixed arrowhead wedge angle (radians). Arrow direction is fully
1010
// determined by u,v (see angPx below); this constant only controls the
@@ -182,12 +182,12 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
182182
// Apply styling using Plotly's standard styling system
183183
var marker = trace.marker || {};
184184
var markerLine = marker.line || {};
185-
var lineColor = Lib.isArrayOrTypedArray(marker.color) ? undefined : marker.color;
185+
const lineColor = Lib.isArrayOrTypedArray(marker.color) ? undefined : marker.color;
186186
Drawing.lineGroupStyle(lineSegments, markerLine.width, lineColor, markerLine.dash);
187187

188-
// If colorscale present, color arrows by marker.color or magnitude |(u,v)|.
188+
// Color arrows from a colorscale or from a discrete marker.color array.
189189
// Shared with style.js so the static render and restyle stay in sync.
190-
if(trace._hasColorscale) colorscaleStroke(lineSegments, trace);
190+
strokeArrowColors(lineSegments, trace);
191191

192192
// Render text labels at data points
193193
var textGroup = d3.select(element).selectAll('g.text')

‎src/traces/quiver/style.js‎

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var Lib = require('../../lib');
55

66
var Drawing = require('../../components/drawing');
77
var Colorscale = require('../../components/colorscale');
8+
const Color = require('../../components/color');
89
var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;
910

1011
// Stroke each arrow path according to the trace colorscale, using marker.color
@@ -33,6 +34,29 @@ function colorscaleStroke(paths, trace) {
3334
});
3435
}
3536

37+
// A color-string array is not a colorscale. Apply each string here, because
38+
// `lineGroupStyle` paints an array as black.
39+
const strokeArrowColors = (paths, trace) => {
40+
if (trace._hasColorscale) {
41+
colorscaleStroke(paths, trace);
42+
return;
43+
}
44+
45+
const markerColor = (trace.marker || {}).color;
46+
if (!Lib.isArrayOrTypedArray(markerColor)) return;
47+
48+
paths.style('stroke', function (cdi) {
49+
if (markerColor.length > cdi.i) {
50+
const color = markerColor[cdi.i];
51+
if (Color.isValid(color)) {
52+
cdi.mcc = color;
53+
return color;
54+
}
55+
}
56+
return this.style.stroke;
57+
});
58+
};
59+
3660
function style(gd) {
3761
var s = d3.select(gd).selectAll('g.trace.quiver');
3862
s.each(function(d) {
@@ -49,7 +73,6 @@ function styleArrows(gd, cd, sel) {
4973
var marker = trace.marker || {};
5074
var markerLine = marker.line || {};
5175
var lineColor = Lib.isArrayOrTypedArray(marker.color) ? undefined : marker.color;
52-
var hasColorscale = trace._hasColorscale;
5376

5477
if(!sel) return;
5578

@@ -78,7 +101,7 @@ function styleArrows(gd, cd, sel) {
78101
// color, so we keep each arrow's color and dim unselected arrows
79102
// via stroke-opacity instead.
80103
Drawing.lineGroupStyle(path, lineWidth, lineColor, markerLine.dash);
81-
if(hasColorscale) colorscaleStroke(path, trace);
104+
strokeArrowColors(path, trace);
82105
path.style('stroke-opacity', dim ? DESELECTDIM : 1);
83106
}
84107
});
@@ -87,14 +110,15 @@ function styleArrows(gd, cd, sel) {
87110
} else {
88111
var paths = sel.selectAll('path.js-line');
89112
paths.call(Drawing.lineGroupStyle, markerLine.width, lineColor, markerLine.dash);
90-
if(hasColorscale) colorscaleStroke(paths, trace);
113+
strokeArrowColors(paths, trace);
91114
paths.style('stroke-opacity', 1);
92115
Drawing.textPointStyle(sel.selectAll('text'), trace, gd);
93116
}
94117
}
95118

96119
module.exports = {
97-
style: style,
98-
styleOnSelect: styleOnSelect,
99-
colorscaleStroke: colorscaleStroke
120+
style,
121+
styleOnSelect,
122+
colorscaleStroke,
123+
strokeArrowColors
100124
};

‎test/jasmine/tests/quiver_test.js‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ var mouseEvent = require('../assets/mouse_event');
99

1010
var customAssertions = require('../assets/custom_assertions');
1111
var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
12+
var quiverStyle = require('../../../src/traces/quiver/style');
1213

1314
describe('Test quiver defaults', function() {
1415
var gd;
@@ -305,6 +306,52 @@ describe('Test quiver interactions', function() {
305306
expect(window.getComputedStyle(arrow).stroke).toBe('rgb(33, 145, 140)', 'arrow');
306307
});
307308

309+
it('should color each arrow from a discrete marker.color array', async () => {
310+
const colors = ['rgb(23, 103, 194)', '#FF4136', '#2ECC40'];
311+
const expected = ['rgb(23, 103, 194)', 'rgb(255, 65, 54)', 'rgb(46, 204, 64)'];
312+
313+
await Plotly.newPlot(gd, [{
314+
type: 'quiver',
315+
x: [1, 2, 3],
316+
y: [1, 2, 3],
317+
u: [1, 1, 1],
318+
v: [0, 1, 0],
319+
marker: {
320+
color: colors,
321+
line: { width: 3 }
322+
}
323+
}]);
324+
325+
function arrowStrokes() {
326+
const arrows = gd.querySelectorAll('g.trace.quiver path.js-line');
327+
expect(arrows.length).toBe(3);
328+
const strokes = [];
329+
for(let i = 0; i < arrows.length; i++) {
330+
strokes.push(window.getComputedStyle(arrows[i]).stroke);
331+
}
332+
return strokes;
333+
}
334+
335+
expect(arrowStrokes()).toEqual(expected);
336+
337+
// A style-only edit must keep the per-arrow colors.
338+
await Plotly.restyle(gd, 'marker.line.width', 4);
339+
expect(arrowStrokes()).toEqual(expected);
340+
341+
// Selection styling must keep each arrow color and only dim the rest.
342+
const cd = gd.calcdata[0];
343+
cd[0].trace.selectedpoints = [0, 2];
344+
cd[0].selected = 1;
345+
cd[2].selected = 1;
346+
quiverStyle.styleOnSelect(gd, cd, cd[0].node3);
347+
348+
expect(arrowStrokes()).toEqual(expected);
349+
const arrows = gd.querySelectorAll('g.trace.quiver path.js-line');
350+
expect(window.getComputedStyle(arrows[0]).strokeOpacity).toBe('1');
351+
expect(window.getComputedStyle(arrows[1]).strokeOpacity).toBe('0.2');
352+
expect(window.getComputedStyle(arrows[2]).strokeOpacity).toBe('1');
353+
});
354+
308355
it('should render multiple quiver traces', function(done) {
309356
Plotly.newPlot(gd, [{
310357
type: 'quiver',
@@ -330,6 +377,12 @@ describe('Test quiver interactions', function() {
330377
.then(function() {
331378
var quiverLayers = gd._fullLayout._plots.xy.plot.selectAll('.trace.quiver');
332379
expect(quiverLayers.size()).toBe(2);
380+
381+
var arrows = gd.querySelectorAll('g.trace.quiver path.js-line');
382+
expect(window.getComputedStyle(arrows[0]).stroke).toBe('rgb(255, 0, 0)');
383+
expect(window.getComputedStyle(arrows[1]).stroke).toBe('rgb(255, 0, 0)');
384+
expect(window.getComputedStyle(arrows[2]).stroke).toBe('rgb(0, 0, 255)');
385+
expect(window.getComputedStyle(arrows[3]).stroke).toBe('rgb(0, 0, 255)');
333386
})
334387
.then(done, done.fail);
335388
});

0 commit comments

Comments
 (0)