diff --git a/src/components/drawing/attributes.js b/src/components/drawing/attributes.js
index 2f73a55daf0..0d3447c620d 100644
--- a/src/components/drawing/attributes.js
+++ b/src/components/drawing/attributes.js
@@ -28,13 +28,46 @@ exports.pattern = {
'By default, no pattern is used for filling the area.',
].join(' ')
},
+ fillmode: {
+ valType: 'enumerated',
+ values: ['replace', 'overlay'],
+ dflt: 'replace',
+ editType: 'style',
+ description: [
+ 'Determines whether `marker.color` should be used',
+ 'as a default to `bgcolor` or a `fgcolor`.'
+ ].join(' ')
+ },
bgcolor: {
valType: 'color',
arrayOk: true,
editType: 'style',
description: [
- 'Sets the background color of the pattern fill.',
- 'Defaults to a transparent background.',
+ 'When there is no colorscale sets the color of background pattern fill.',
+ 'Defaults to a `marker.color` background when `fillmode` is *overlay*.',
+ 'Otherwise, defaults to a transparent background.'
+ ].join(' ')
+ },
+ fgcolor: {
+ valType: 'color',
+ arrayOk: true,
+ editType: 'style',
+ description: [
+ 'When there is no colorscale sets the color of foreground pattern fill.',
+ 'Defaults to a `marker.color` background when `fillmode` is *replace*.',
+ 'Otherwise, defaults to dark grey or white',
+ 'to increase contrast with the `bgcolor`.',
+ ].join(' ')
+ },
+ fgopacity: {
+ valType: 'number',
+ editType: 'style',
+ min: 0,
+ max: 1,
+ description: [
+ 'Sets the opacity of the foreground pattern fill.',
+ 'Defaults to a 0.5 when `fillmode` is *overlay*.',
+ 'Otherwise, defaults to 1.'
].join(' ')
},
size: {
@@ -62,5 +95,8 @@ exports.pattern = {
'and solidty of 1 shows only the foreground color without pattern.',
].join(' ')
},
- editType: 'style'
+ editType: 'style',
+ description: [
+ 'Sets the pattern within the marker.'
+ ].join(' '),
};
diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js
index 484539f20de..f1e6129f3ee 100644
--- a/src/components/drawing/index.js
+++ b/src/components/drawing/index.js
@@ -359,16 +359,31 @@ drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) {
*
* @param {object} sel: d3 selection to apply this pattern to
* You can use `selection.call(Drawing.pattern, ...)`
+ * @param {string} calledBy: option to know the caller component
* @param {DOM element} gd: the graph div `sel` is part of
* @param {string} patternID: a unique (within this plot) identifier
* for this pattern, so that we don't create unnecessary definitions
- * @param {string} bgcolor: background color for this pattern
- * @param {string} fgcolor: foreground color for this pattern
* @param {number} size: size of unit squares for repetition of this pattern
* @param {number} solidity: how solid lines of this pattern are
- * @param {string} prop: the property to apply to, 'fill' or 'stroke'
+ * @param {string} mcc: color when painted with colorscale
+ * @param {string} fillmode: fillmode for this pattern
+ * @param {string} bgcolor: background color for this pattern
+ * @param {string} fgcolor: foreground color for this pattern
+ * @param {number} fgopacity: foreground opacity for this pattern
*/
-drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, solidity, prop) {
+drawing.pattern = function(sel, calledBy, gd, patternID, shape, size, solidity, mcc, fillmode, bgcolor, fgcolor, fgopacity) {
+ var isLegend = calledBy === 'legend';
+
+ if(mcc) {
+ if(fillmode === 'overlay') {
+ bgcolor = mcc;
+ fgcolor = Color.contrast(bgcolor);
+ } else {
+ bgcolor = undefined;
+ fgcolor = mcc;
+ }
+ }
+
var fullLayout = gd._fullLayout;
var fullID = 'p' + fullLayout._uid + '-' + patternID;
var width, height;
@@ -392,6 +407,7 @@ drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, so
patternTag = 'path';
patternAttrs = {
'd': path,
+ 'opacity': fgopacity,
'stroke': fgcolor,
'stroke-width': linewidth + 'px'
};
@@ -406,6 +422,7 @@ drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, so
patternTag = 'path';
patternAttrs = {
'd': path,
+ 'opacity': fgopacity,
'stroke': fgcolor,
'stroke-width': linewidth + 'px'
};
@@ -423,6 +440,7 @@ drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, so
patternTag = 'path';
patternAttrs = {
'd': path,
+ 'opacity': fgopacity,
'stroke': fgcolor,
'stroke-width': linewidth + 'px'
};
@@ -436,6 +454,7 @@ drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, so
patternTag = 'path';
patternAttrs = {
'd': path,
+ 'opacity': fgopacity,
'stroke': fgcolor,
'stroke-width': linewidth + 'px'
};
@@ -449,6 +468,7 @@ drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, so
patternTag = 'path';
patternAttrs = {
'd': path,
+ 'opacity': fgopacity,
'stroke': fgcolor,
'stroke-width': linewidth + 'px'
};
@@ -463,6 +483,7 @@ drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, so
patternTag = 'path';
patternAttrs = {
'd': path,
+ 'opacity': fgopacity,
'stroke': fgcolor,
'stroke-width': linewidth + 'px'
};
@@ -480,14 +501,23 @@ drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, so
'cx': width / 2,
'cy': height / 2,
'r': radius,
+ 'opacity': fgopacity,
'fill': fgcolor
};
break;
}
+ var str = [
+ shape || 'noSh',
+ bgcolor || 'noBg',
+ fgcolor || 'noFg',
+ size,
+ solidity
+ ].join(';');
+
var pattern = fullLayout._defs.select('.patterns')
.selectAll('#' + fullID)
- .data([shape + ';' + bgcolor + ';' + fgcolor + ';' + size + ';' + solidity], Lib.identity);
+ .data([str], Lib.identity);
pattern.exit().remove();
@@ -500,7 +530,9 @@ drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, so
'id': fullID,
'width': width + 'px',
'height': height + 'px',
- 'patternUnits': 'userSpaceOnUse'
+ 'patternUnits': 'userSpaceOnUse',
+ // for legends scale down patterns just a bit so that default size (i.e 8) nicely fit in small icons
+ 'patternTransform': isLegend ? 'scale(0.8)' : ''
});
if(bgcolor) {
@@ -522,8 +554,8 @@ drawing.pattern = function(sel, gd, patternID, shape, bgcolor, fgcolor, size, so
.attr(patternAttrs);
});
- sel.style(prop, getFullUrl(fullID, gd))
- .style(prop + '-opacity', null);
+ sel.style('fill', getFullUrl(fullID, gd))
+ .style('fill-opacity', null);
sel.classed('pattern_filled', true);
var className2query = function(s) {
@@ -693,18 +725,25 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
[[0, gradientColor], [1, fillColor]], 'fill');
} else if(patternShape) {
var patternBGColor = drawing.getPatternAttr(markerPattern.bgcolor, d.i, null);
+ var patternFGColor = drawing.getPatternAttr(markerPattern.fgcolor, d.i, null);
+ var patternFGOpacity = markerPattern.fgopacity;
var patternSize = drawing.getPatternAttr(markerPattern.size, d.i, 8);
var patternSolidity = drawing.getPatternAttr(markerPattern.solidity, d.i, 0.3);
- var perPointPattern = Lib.isArrayOrTypedArray(markerPattern.shape) ||
- Lib.isArrayOrTypedArray(markerPattern.bgcolor) ||
- Lib.isArrayOrTypedArray(markerPattern.size) ||
- Lib.isArrayOrTypedArray(markerPattern.solidity);
+ var perPointPattern = d.mcc ||
+ Lib.isArrayOrTypedArray(markerPattern.shape) ||
+ Lib.isArrayOrTypedArray(markerPattern.bgcolor) ||
+ Lib.isArrayOrTypedArray(markerPattern.size) ||
+ Lib.isArrayOrTypedArray(markerPattern.solidity);
var patternID = trace.uid;
if(perPointPattern) patternID += '-' + d.i;
- drawing.pattern(sel, gd, patternID, patternShape, patternBGColor, fillColor,
- patternSize, patternSolidity, 'fill');
+ drawing.pattern(
+ sel, 'point', gd, patternID,
+ patternShape, patternSize, patternSolidity,
+ d.mcc, markerPattern.fillmode,
+ patternBGColor, patternFGColor, patternFGOpacity
+ );
} else {
Color.fill(sel, fillColor);
}
diff --git a/src/components/legend/style.js b/src/components/legend/style.js
index 06f31e920f1..9ca9c194af1 100644
--- a/src/components/legend/style.js
+++ b/src/components/legend/style.js
@@ -348,18 +348,33 @@ module.exports = function style(s, gd, legend) {
p.style('stroke-width', w + 'px');
- var fillColor = d0.mc || marker.color;
+ var mcc = d0.mcc;
+ if(!legend._inHover && 'mc' in d0) {
+ // not in unified hover but
+ // for legend use the color in the middle of scale
+ var cOpts = extractOpts(marker);
+ var mid = cOpts.mid;
+ if(mid === undefined) mid = (cOpts.max + cOpts.min) / 2;
+ mcc = Drawing.tryColorscale(marker, '')(mid);
+ }
+ var fillColor = mcc || d0.mc || marker.color;
var markerPattern = marker.pattern;
var patternShape = markerPattern && Drawing.getPatternAttr(markerPattern.shape, 0, '');
if(patternShape) {
var patternBGColor = Drawing.getPatternAttr(markerPattern.bgcolor, 0, null);
- var patternSize = Math.min(12, Drawing.getPatternAttr(markerPattern.size, 0, 8));
- var patternSolidity = Drawing.getPatternAttr(markerPattern.solidity, 0, 0.3);
+ var patternFGColor = Drawing.getPatternAttr(markerPattern.fgcolor, 0, null);
+ var patternFGOpacity = markerPattern.fgopacity;
+ var patternSize = dimAttr(markerPattern.size, 8, 10);
+ var patternSolidity = dimAttr(markerPattern.solidity, 0.5, 1);
var patternID = 'legend-' + trace.uid;
- p.call(Drawing.pattern, gd, patternID, patternShape, patternBGColor,
- fillColor, patternSize, patternSolidity, 'fill');
+ p.call(
+ Drawing.pattern, 'legend', gd, patternID,
+ patternShape, patternSize, patternSolidity,
+ mcc, markerPattern.fillmode,
+ patternBGColor, patternFGColor, patternFGOpacity
+ );
} else {
p.call(Color.fill, fillColor);
}
@@ -662,3 +677,9 @@ function getStyleGuide(d) {
anyFill: showFill || showGradientFill,
};
}
+
+function dimAttr(v, dflt, max) {
+ if(v && Lib.isArrayOrTypedArray(v)) return dflt;
+ if(v > max) return max;
+ return v;
+}
diff --git a/src/lib/coerce.js b/src/lib/coerce.js
index d6273cd74cd..95705c8c8a3 100644
--- a/src/lib/coerce.js
+++ b/src/lib/coerce.js
@@ -5,6 +5,7 @@ var tinycolor = require('tinycolor2');
var baseTraceAttrs = require('../plots/attributes');
var colorscales = require('../components/colorscale/scales');
+var Color = require('../components/color');
var DESELECTDIM = require('../constants/interactions').DESELECTDIM;
var nestedProperty = require('./nested_property');
@@ -427,12 +428,30 @@ exports.coerceFont = function(coerce, attr, dfltObj) {
/*
* Shortcut to coerce the pattern attributes
*/
-exports.coercePattern = function(coerce, attr) {
+exports.coercePattern = function(coerce, attr, markerColor, hasMarkerColorscale) {
var shape = coerce(attr + '.shape');
if(shape) {
- coerce(attr + '.size');
- coerce(attr + '.bgcolor');
coerce(attr + '.solidity');
+ coerce(attr + '.size');
+ var fillmode = coerce(attr + '.fillmode');
+ var isOverlay = fillmode === 'overlay';
+
+ if(!hasMarkerColorscale) {
+ var bgcolor = coerce(attr + '.bgcolor', isOverlay ?
+ markerColor :
+ undefined
+ );
+
+ coerce(attr + '.fgcolor', isOverlay ?
+ Color.contrast(bgcolor) :
+ markerColor
+ );
+ }
+
+ coerce(attr + '.fgopacity', isOverlay ?
+ 0.5 :
+ 1
+ );
}
};
diff --git a/src/traces/bar/style_defaults.js b/src/traces/bar/style_defaults.js
index 04588b39ea6..6fbf26606b6 100644
--- a/src/traces/bar/style_defaults.js
+++ b/src/traces/bar/style_defaults.js
@@ -6,9 +6,9 @@ var colorscaleDefaults = require('../../components/colorscale/defaults');
var coercePattern = require('../../lib').coercePattern;
module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout) {
- coerce('marker.color', defaultColor);
-
- if(hasColorscale(traceIn, 'marker')) {
+ var markerColor = coerce('marker.color', defaultColor);
+ var hasMarkerColorscale = hasColorscale(traceIn, 'marker');
+ if(hasMarkerColorscale) {
colorscaleDefaults(
traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'}
);
@@ -24,8 +24,7 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, default
coerce('marker.line.width');
coerce('marker.opacity');
- coercePattern(coerce, 'marker.pattern');
-
+ coercePattern(coerce, 'marker.pattern', markerColor, hasMarkerColorscale);
coerce('selected.marker.color');
coerce('unselected.marker.color');
};
diff --git a/tasks/test_mock.js b/tasks/test_mock.js
index c1b2f6f9544..957b86b41ef 100644
--- a/tasks/test_mock.js
+++ b/tasks/test_mock.js
@@ -226,6 +226,8 @@ function notBlackListed(name) {
'mathjax',
'ohlc_first',
'pattern_bars',
+ 'pattern_fgcolor_overlay_fillmode',
+ 'pattern_with_colorscale',
'plot_types',
'polar_blank',
'polar_dates',
diff --git a/test/image/baselines/pattern_bars.png b/test/image/baselines/pattern_bars.png
index 2a93a12acd3..48265b159ae 100644
Binary files a/test/image/baselines/pattern_bars.png and b/test/image/baselines/pattern_bars.png differ
diff --git a/test/image/baselines/pattern_fgcolor_overlay_fillmode.png b/test/image/baselines/pattern_fgcolor_overlay_fillmode.png
new file mode 100644
index 00000000000..a38e3e8a19f
Binary files /dev/null and b/test/image/baselines/pattern_fgcolor_overlay_fillmode.png differ
diff --git a/test/image/baselines/pattern_legend_variations.png b/test/image/baselines/pattern_legend_variations.png
new file mode 100644
index 00000000000..55c0cc29a37
Binary files /dev/null and b/test/image/baselines/pattern_legend_variations.png differ
diff --git a/test/image/baselines/pattern_with_colorscale.png b/test/image/baselines/pattern_with_colorscale.png
new file mode 100644
index 00000000000..98109dee60e
Binary files /dev/null and b/test/image/baselines/pattern_with_colorscale.png differ
diff --git a/test/image/mocks/pattern_fgcolor_overlay_fillmode.json b/test/image/mocks/pattern_fgcolor_overlay_fillmode.json
new file mode 100644
index 00000000000..7c7da5271ac
--- /dev/null
+++ b/test/image/mocks/pattern_fgcolor_overlay_fillmode.json
@@ -0,0 +1,221 @@
+{
+ "data": [
+ {
+ "x": ["a", "b", "c", "d", "e"],
+ "y": [1, 2, 3, 4, 5],
+ "name": "Bar 1",
+ "type": "bar",
+ "textposition": "outside",
+ "text": "bg+fg
color",
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": "/",
+ "bgcolor": ["white", "lightblue", "blue", "darkblue", "black"],
+ "fgcolor": ["black", "darkblue", "blue", "lightblue", "white"]
+ }
+ }
+ },
+ {
+ "x": ["a", "b", "c", "d", "e"],
+ "y": [2, 3, 4, 5, 6],
+ "name": "Bar 2",
+ "type": "bar",
+ "textposition": "outside",
+ "text": "shape",
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": ["|", "/", "-", "\\", "|"]
+ }
+ }
+ },
+ {
+ "x": ["a", "b", "c", "d", "e"],
+ "y": [3, 4, 5, 6, 7],
+ "name": "Bar 3",
+ "type": "bar",
+ "textposition": "outside",
+ "text": "size",
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": "x",
+ "size": [4, 6, 8, 10, 12]
+ }
+ }
+ },
+ {
+ "x": ["a", "b", "c", "d", "e"],
+ "y": [6, 7, 8, 9, 10],
+ "name": "Bar 4",
+ "type": "bar",
+ "textposition": "outside",
+ "text": "solidity",
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": ".",
+ "bgcolor": "yellow",
+ "solidity": [0.1, 0.3, 0.5, 0.7, 0.9]
+ }
+ }
+ },
+
+ {
+ "t": ["M", "N", "O", "P"],
+ "r": [1, 2, 3, 4],
+ "type": "barpolar",
+ "name": "Barpolar 1",
+ "marker": {
+ "color": "red",
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": "+",
+ "size": [1, 2, 3, 4]
+ }
+ }
+ },
+ {
+ "t": ["M", "N", "O", "P"],
+ "r": [2, 3, 4, 1],
+ "type": "barpolar",
+ "name": "Barpolar 2",
+ "marker": {
+ "color": "rgba(0,127,0,0.5)",
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": "x",
+ "solidity": 0.75
+ }
+ }
+ },
+ {
+ "t": ["M", "N", "O", "P"],
+ "r": [3, 4, 1, 2],
+ "type": "barpolar",
+ "name": "Barpolar 3",
+ "marker": {
+ "color": "blue",
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": ["|", "-", "|", "-"],
+ "solidity": 0.5
+ }
+ }
+ },
+ {
+ "t": ["M", "N", "O", "P"],
+ "r": [4, 1, 2, 3],
+ "type": "barpolar",
+ "name": "Barpolar 4",
+ "marker": {
+ "color": "orange",
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": ".",
+ "bgcolor": "yellow",
+ "solidity": [0.2, 0.8, 0.6, 0.4]
+ }
+ }
+ },
+
+ {
+ "xaxis": "x2",
+ "yaxis": "y2",
+ "y": ["A", "A", "A", "A", "B", "B", "C"],
+ "name": "Histogram 1",
+ "type": "histogram",
+ "marker": {
+ "color": "yellow",
+ "line": {
+ "color": "black",
+ "width": 2
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "fgcolor": "blue",
+ "shape": "."
+ }
+ }
+ },
+ {
+ "xaxis": "x2",
+ "yaxis": "y2",
+ "y": ["C", "C", "C", "C", "B", "B", "A"],
+ "name": "Histogram 2",
+ "type": "histogram",
+ "marker": {
+ "color": "yellow",
+ "line": {
+ "color": "red",
+ "width": 4
+ },
+ "pattern": {
+ "fillmode": "replace",
+ "fgcolor": "green",
+ "shape": "x"
+ }
+ }
+ },
+
+ {
+ "xaxis": "x3",
+ "yaxis": "y3",
+ "x": [3, 2, 1],
+ "y": ["U", "V", "W"],
+ "name": "Funnel",
+ "type": "funnel",
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "solidity": [0.25, 0.5, 0.75],
+ "shape": ["|", "", "-"],
+ "bgcolor": "black"
+ }
+ }
+ }
+ ],
+ "layout": {
+ "title": {
+ "text": "pattern options - fillmode: 'overlay'"
+ },
+ "width": 1000,
+ "height": 600,
+
+ "xaxis": {
+ "domain": [0, 1]
+ },
+ "yaxis": {
+ "range": [0, 11],
+ "domain": [0, 0.475]
+ },
+
+ "polar": {
+ "domain": {
+ "x": [0.35, 0.65],
+ "y": [0.525, 1]
+ }
+ },
+
+ "xaxis2": {
+ "anchor": "y2",
+ "gridcolor": "black",
+ "gridwidth": 2,
+ "domain": [0, 0.3]
+ },
+ "yaxis2": {
+ "anchor": "x2",
+ "domain": [0.525, 1]
+ },
+
+ "xaxis3": {
+ "anchor": "y3",
+ "domain": [0.7, 1]
+ },
+ "yaxis3": {
+ "anchor": "x3",
+ "domain": [0.525, 1]
+ }
+ }
+}
diff --git a/test/image/mocks/pattern_legend_variations.json b/test/image/mocks/pattern_legend_variations.json
new file mode 100644
index 00000000000..f81949ab961
--- /dev/null
+++ b/test/image/mocks/pattern_legend_variations.json
@@ -0,0 +1,108 @@
+{
+ "data": [
+ {
+ "name": "size: 4",
+ "type": "bar",
+ "x": ["A", "B", "C", "D", "E"],
+ "y": [1, 1, 1, 1, 1],
+ "marker": {
+ "colorscale": "Greens", "reversescale": true,
+ "line": { "color": "#444", "width": 1 },
+ "color": [1, 2, 3, 4, 5],
+ "pattern": {
+ "fillmode": "replace",
+ "size": 4,
+ "shape": "."
+ }
+ }
+ },
+ {
+ "name": "size: 6",
+ "type": "bar",
+ "x": ["A", "B", "C", "D", "E"],
+ "y": [1, 1, 1, 1, 1],
+ "marker": {
+ "colorscale": "Greens", "reversescale": true,
+ "line": { "color": "#444", "width": 1 },
+ "color": [1, 2, 3, 4, 5],
+ "pattern": {
+ "fillmode": "overlay", "fgopacity": 1,
+ "size": 6,
+ "shape": "."
+ }
+ }
+ },
+ {
+ "name": "size: 8",
+ "type": "bar",
+ "x": ["A", "B", "C", "D", "E"],
+ "y": [1, 1, 1, 1, 1],
+ "marker": {
+ "colorscale": "Reds",
+ "line": { "color": "#444", "width": 1 },
+ "color": [1, 2, 3, 4, 5],
+ "pattern": {
+ "fillmode": "replace",
+ "size": 8,
+ "shape": "."
+ }
+ }
+ },
+ {
+ "name": "size: 10",
+ "type": "bar",
+ "x": ["A", "B", "C", "D", "E"],
+ "y": [1, 1, 1, 1, 1],
+ "marker": {
+ "colorscale": "Reds",
+ "line": { "color": "#444", "width": 1 },
+ "color": [1, 2, 3, 4, 5],
+ "pattern": {
+ "fillmode": "overlay", "fgopacity": 1,
+ "size": 10,
+ "shape": "."
+ }
+ }
+ },
+ {
+ "name": "size: 12",
+ "type": "bar",
+ "x": ["A", "B", "C", "D", "E"],
+ "y": [1, 1, 1, 1, 1],
+ "marker": {
+ "colorscale": "Blues", "reversescale": true,
+ "line": { "color": "#444", "width": 1 },
+ "color": [1, 2, 3, 4, 5],
+ "pattern": {
+ "fillmode": "replace",
+ "size": 12,
+ "shape": "."
+ }
+ }
+ },
+ {
+ "name": "size: [ ]",
+ "type": "bar",
+ "x": ["A", "B", "C", "D", "E"],
+ "y": [1, 1, 1, 1, 1],
+ "marker": {
+ "colorscale": "Blues", "reversescale": true,
+ "line": { "color": "#444", "width": 1 },
+ "color": [1, 2, 3, 4, 5],
+ "pattern": {
+ "fillmode": "overlay", "fgopacity": 1,
+ "size": [4, 6, 8, 10, 12],
+ "shape": "."
+ }
+ }
+ }
+ ],
+ "layout": {
+ "title": {
+ "text": "legend pattern size range"
+ },
+ "width": 800,
+ "height": 400,
+ "hovermode": "x unified"
+ }
+}
diff --git a/test/image/mocks/pattern_with_colorscale.json b/test/image/mocks/pattern_with_colorscale.json
new file mode 100644
index 00000000000..7a37c339f5a
--- /dev/null
+++ b/test/image/mocks/pattern_with_colorscale.json
@@ -0,0 +1,106 @@
+{
+ "data": [
+ {
+ "legendrank": 4,
+ "x": ["a", "b", "c", "d"],
+ "y": [4, 3, 2, 1],
+ "name": "replace bar",
+ "type": "bar",
+ "marker": {
+ "line": { "color": "lightblue", "width": 2 },
+ "color": [4, 3, 2, 1],
+ "pattern": {
+ "fillmode": "replace",
+ "shape": "/"
+ }
+ }
+ },
+ {
+ "legendrank": 3,
+ "xaxis": "x2",
+ "yaxis": "y2",
+ "x": ["a", "b", "c", "d"],
+ "y": [4, 3, 2, 1],
+ "name": "overlay bar",
+ "type": "bar",
+ "marker": {
+ "line": { "color": "lightblue", "width": 2 },
+ "color": [4, 3, 2, 1],
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": "/"
+ }
+ }
+ },
+
+ {
+ "legendrank": 2,
+ "t": ["M", "N", "O", "P"],
+ "r": [4, 3, 2, 1],
+ "type": "barpolar",
+ "name": "replace polar",
+ "marker": {
+ "line": { "color": "lightblue", "width": 2 },
+ "color": [4, 3, 2, 1],
+ "pattern": {
+ "fillmode": "replace",
+ "shape": "."
+ }
+ }
+ },
+
+ {
+ "legendrank": 1,
+ "subplot": "polar2",
+ "t": ["M", "N", "O", "P"],
+ "r": [4, 3, 2, 1],
+ "type": "barpolar",
+ "name": "overlay polar",
+ "marker": {
+ "line": { "color": "lightblue", "width": 2 },
+ "color": [4, 3, 2, 1],
+ "pattern": {
+ "fillmode": "overlay",
+ "shape": "."
+ }
+ }
+ }
+ ],
+ "layout": {
+ "title": {
+ "text": "pattern with colorscale and different fillmodes"
+ },
+ "width": 600,
+ "height": 600,
+
+ "xaxis": {
+ "domain": [0, 1]
+ },
+ "yaxis": {
+ "domain": [0, 0.3]
+ },
+
+ "xaxis2": {
+ "anchor": "y2",
+ "domain": [0, 1]
+ },
+ "yaxis2": {
+ "anchor": "x2",
+ "domain": [0.35, 0.65]
+ },
+
+ "polar": {
+ "domain": {
+ "x": [0, 0.475],
+ "y": [0.7, 1]
+ }
+ },
+
+ "polar2": {
+ "domain": {
+ "x": [0.525, 1],
+ "y": [0.7, 1]
+ }
+ }
+ }
+}
diff --git a/test/jasmine/tests/bar_test.js b/test/jasmine/tests/bar_test.js
index 62d74d99d71..9108604698d 100644
--- a/test/jasmine/tests/bar_test.js
+++ b/test/jasmine/tests/bar_test.js
@@ -253,6 +253,90 @@ describe('Bar.supplyDefaults', function() {
supplyAllDefaults(gd);
expect(gd._fullLayout.barmode).toBe('group', '`barmode` should be set to its default ');
});
+
+ it('bgcolor & fgcolor defaults with *replace* pattern.fillmode', function() {
+ traceIn = {
+ marker: {
+ color: 'green',
+ pattern: {
+ shape: '+'
+ }
+ },
+ y: [1]
+ };
+ var layout = {
+ font: {family: 'arial', color: '#AAA', size: 13}
+ };
+
+ supplyDefaults(traceIn, traceOut, defaultColor, layout);
+
+ expect(traceOut.marker.pattern.bgcolor).toBeUndefined('transparent background');
+ expect(traceOut.marker.pattern.fgcolor).toBe('green');
+ expect(traceOut.marker.pattern.fgopacity).toBe(1);
+ });
+
+ it('bgcolor & fgcolor defaults with *overlay* pattern.fillmode', function() {
+ traceIn = {
+ marker: {
+ color: 'green',
+ pattern: {
+ fillmode: 'overlay',
+ shape: '+'
+ }
+ },
+ y: [1]
+ };
+ var layout = {
+ font: {family: 'arial', color: '#AAA', size: 13}
+ };
+
+ supplyDefaults(traceIn, traceOut, defaultColor, layout);
+
+ expect(traceOut.marker.pattern.bgcolor).toBe('green');
+ expect(traceOut.marker.pattern.fgcolor).toBe('#fff');
+ expect(traceOut.marker.pattern.fgopacity).toBe(0.5);
+ });
+
+ it('should not coerce marker.pattern.bgcolor and marker.pattern.fgcolor when marker.colorscale is present - case of *replace* fillmode', function() {
+ traceIn = {
+ marker: {
+ colorscale: 'Blues',
+ pattern: {
+ shape: '+'
+ }
+ },
+ color: [1, 2, 3],
+ y: [1, 2, 3]
+ };
+ var layout = {};
+
+ supplyDefaults(traceIn, traceOut, defaultColor, layout);
+
+ expect(traceOut.marker.pattern.bgcolor).toBeUndefined();
+ expect(traceOut.marker.pattern.fgcolor).toBeUndefined();
+ expect(traceOut.marker.pattern.fgopacity).toBe(1);
+ });
+
+ it('should not coerce marker.pattern.bgcolor and marker.pattern.fgcolor when marker.colorscale is present - case of *overlay* fillmode', function() {
+ traceIn = {
+ marker: {
+ colorscale: 'Blues',
+ pattern: {
+ fillmode: 'overlay',
+ shape: '+'
+ }
+ },
+ color: [1, 2, 3],
+ y: [1, 2, 3]
+ };
+ var layout = {};
+
+ supplyDefaults(traceIn, traceOut, defaultColor, layout);
+
+ expect(traceOut.marker.pattern.bgcolor).toBeUndefined();
+ expect(traceOut.marker.pattern.fgcolor).toBeUndefined();
+ expect(traceOut.marker.pattern.fgopacity).toBe(0.5);
+ });
});
describe('bar calc / crossTraceCalc (formerly known as setPositions)', function() {