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/8069_fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Fix quiver arrow colors for a discrete `marker.color` array [[#8069](https://github.com/plotly/plotly.js/pull/8069)]
8 changes: 4 additions & 4 deletions src/traces/quiver/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var d3 = require('@plotly/d3');

var Lib = require('../../lib');
var Drawing = require('../../components/drawing');
var colorscaleStroke = require('./style').colorscaleStroke;
var strokeArrowColors = require('./style').strokeArrowColors;

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

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

// Render text labels at data points
var textGroup = d3.select(element).selectAll('g.text')
Expand Down
32 changes: 28 additions & 4 deletions src/traces/quiver/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var Lib = require('../../lib');

var Drawing = require('../../components/drawing');
var Colorscale = require('../../components/colorscale');
var Color = require('../../components/color');
var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;

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

// A color-string array is not a colorscale. Apply each string here, because
// `lineGroupStyle` paints an array as black.
const strokeArrowColors = (paths, trace) => {
if(trace._hasColorscale) {
colorscaleStroke(paths, trace);
return;
}

const markerColor = (trace.marker || {}).color;
if(!Lib.isArrayOrTypedArray(markerColor)) return;

paths.style('stroke', function(cdi) {
if(markerColor.length > cdi.i) {
const color = markerColor[cdi.i];
if(Color.isValid(color)) {
cdi.mcc = color;
return color;
}
}
return this.style.stroke;
});
};

function style(gd) {
var s = d3.select(gd).selectAll('g.trace.quiver');
s.each(function(d) {
Expand All @@ -49,7 +73,6 @@ function styleArrows(gd, cd, sel) {
var marker = trace.marker || {};
var markerLine = marker.line || {};
var lineColor = Lib.isArrayOrTypedArray(marker.color) ? undefined : marker.color;
var hasColorscale = trace._hasColorscale;

if(!sel) return;

Expand Down Expand Up @@ -78,7 +101,7 @@ function styleArrows(gd, cd, sel) {
// color, so we keep each arrow's color and dim unselected arrows
// via stroke-opacity instead.
Drawing.lineGroupStyle(path, lineWidth, lineColor, markerLine.dash);
if(hasColorscale) colorscaleStroke(path, trace);
strokeArrowColors(path, trace);
path.style('stroke-opacity', dim ? DESELECTDIM : 1);
}
});
Expand All @@ -87,7 +110,7 @@ function styleArrows(gd, cd, sel) {
} else {
var paths = sel.selectAll('path.js-line');
paths.call(Drawing.lineGroupStyle, markerLine.width, lineColor, markerLine.dash);
if(hasColorscale) colorscaleStroke(paths, trace);
strokeArrowColors(paths, trace);
paths.style('stroke-opacity', 1);
Drawing.textPointStyle(sel.selectAll('text'), trace, gd);
}
Expand All @@ -96,5 +119,6 @@ function styleArrows(gd, cd, sel) {
module.exports = {
style: style,
styleOnSelect: styleOnSelect,
colorscaleStroke: colorscaleStroke
colorscaleStroke: colorscaleStroke,
strokeArrowColors: strokeArrowColors
};
53 changes: 53 additions & 0 deletions test/jasmine/tests/quiver_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var mouseEvent = require('../assets/mouse_event');

var customAssertions = require('../assets/custom_assertions');
var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
var quiverStyle = require('../../../src/traces/quiver/style');

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

it('should color each arrow from a discrete marker.color array', async () => {
const colors = ['rgb(23, 103, 194)', '#FF4136', '#2ECC40'];
const expected = ['rgb(23, 103, 194)', 'rgb(255, 65, 54)', 'rgb(46, 204, 64)'];

await Plotly.newPlot(gd, [{
type: 'quiver',
x: [1, 2, 3],
y: [1, 2, 3],
u: [1, 1, 1],
v: [0, 1, 0],
marker: {
color: colors,
line: { width: 3 }
}
}]);

function arrowStrokes() {
const arrows = gd.querySelectorAll('g.trace.quiver path.js-line');
expect(arrows.length).toBe(3);
const strokes = [];
for(let i = 0; i < arrows.length; i++) {
strokes.push(window.getComputedStyle(arrows[i]).stroke);
}
return strokes;
}

expect(arrowStrokes()).toEqual(expected);

// A style-only edit must keep the per-arrow colors.
await Plotly.restyle(gd, 'marker.line.width', 4);
expect(arrowStrokes()).toEqual(expected);

// Selection styling must keep each arrow color and only dim the rest.
const cd = gd.calcdata[0];
cd[0].trace.selectedpoints = [0, 2];
cd[0].selected = 1;
cd[2].selected = 1;
quiverStyle.styleOnSelect(gd, cd, cd[0].node3);

expect(arrowStrokes()).toEqual(expected);
const arrows = gd.querySelectorAll('g.trace.quiver path.js-line');
expect(window.getComputedStyle(arrows[0]).strokeOpacity).toBe('1');
expect(window.getComputedStyle(arrows[1]).strokeOpacity).toBe('0.2');
expect(window.getComputedStyle(arrows[2]).strokeOpacity).toBe('1');
});

it('should render multiple quiver traces', function(done) {
Plotly.newPlot(gd, [{
type: 'quiver',
Expand All @@ -330,6 +377,12 @@ describe('Test quiver interactions', function() {
.then(function() {
var quiverLayers = gd._fullLayout._plots.xy.plot.selectAll('.trace.quiver');
expect(quiverLayers.size()).toBe(2);

var arrows = gd.querySelectorAll('g.trace.quiver path.js-line');
expect(window.getComputedStyle(arrows[0]).stroke).toBe('rgb(255, 0, 0)');
expect(window.getComputedStyle(arrows[1]).stroke).toBe('rgb(255, 0, 0)');
expect(window.getComputedStyle(arrows[2]).stroke).toBe('rgb(0, 0, 255)');
expect(window.getComputedStyle(arrows[3]).stroke).toBe('rgb(0, 0, 255)');
})
.then(done, done.fail);
});
Expand Down
Loading