Skip to content
1 change: 1 addition & 0 deletions draftlogs/7280_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add `pattern.path` attribute as an alternative to the preset `pattern.shape` values, so you can use any SVG path string as a pattern fill. [[#7280](https://github.com/plotly/plotly.js/pull/7280)]
10 changes: 10 additions & 0 deletions src/components/drawing/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ exports.pattern = {
'By default, no pattern is used for filling the area.',
].join(' ')
},
path: {
valType: 'string',
arrayOk: true,
editType: 'style',
description: [
'Sets a custom path for pattern fill.',
'Use with no `shape` or `solidity`, provide an SVG path string for',
'the regions of the square from (0,0) to (`size`,`size`) to color.'
].join(' ')
},
fillmode: {
valType: 'enumerated',
values: ['replace', 'overlay'],
Expand Down
35 changes: 25 additions & 10 deletions src/components/drawing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,14 @@ drawing.dashStyle = function(dash, lineWidth) {
function setFillStyle(sel, trace, gd, forLegend) {
var markerPattern = trace.fillpattern;
var fillgradient = trace.fillgradient;
var patternShape = markerPattern && drawing.getPatternAttr(markerPattern.shape, 0, '');
var pAttr = drawing.getPatternAttr;
var patternShape = markerPattern && (pAttr(markerPattern.shape, 0, '') || pAttr(markerPattern.path, 0, ''));
if(patternShape) {
var patternBGColor = drawing.getPatternAttr(markerPattern.bgcolor, 0, null);
var patternFGColor = drawing.getPatternAttr(markerPattern.fgcolor, 0, null);
var patternBGColor = pAttr(markerPattern.bgcolor, 0, null);
var patternFGColor = pAttr(markerPattern.fgcolor, 0, null);
var patternFGOpacity = markerPattern.fgopacity;
var patternSize = drawing.getPatternAttr(markerPattern.size, 0, 8);
var patternSolidity = drawing.getPatternAttr(markerPattern.solidity, 0, 0.3);
var patternSize = pAttr(markerPattern.size, 0, 8);
var patternSolidity = pAttr(markerPattern.solidity, 0, 0.3);
var patternID = trace.uid;
drawing.pattern(sel, 'point', gd, patternID,
patternShape, patternSize, patternSolidity,
Expand Down Expand Up @@ -662,6 +663,16 @@ drawing.pattern = function(sel, calledBy, gd, patternID, shape, size, solidity,
fill: fgRGB
};
break;
default:
width = size;
height = size;
patternTag = 'path';
patternAttrs = {
d: shape,
opacity: opacity,
fill: fgRGB
};
break;
}

var str = [
Expand Down Expand Up @@ -869,7 +880,10 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd, pt) {
}

var markerPattern = marker.pattern;
var patternShape = markerPattern && drawing.getPatternAttr(markerPattern.shape, d.i, '');
var pAttr = drawing.getPatternAttr;
var patternShape = markerPattern && (
pAttr(markerPattern.shape, d.i, '') || pAttr(markerPattern.path, d.i, '')
);

if(gradientType && gradientType !== 'none') {
var gradientColor = d.mgc;
Expand All @@ -888,14 +902,15 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd, pt) {
fgcolor = pt.color;
perPointPattern = true;
}
var patternFGColor = drawing.getPatternAttr(fgcolor, d.i, (pt && pt.color) || null);
var patternFGColor = pAttr(fgcolor, d.i, (pt && pt.color) || null);

var patternBGColor = drawing.getPatternAttr(markerPattern.bgcolor, d.i, null);
var patternBGColor = pAttr(markerPattern.bgcolor, 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 patternSize = pAttr(markerPattern.size, d.i, 8);
var patternSolidity = pAttr(markerPattern.solidity, d.i, 0.3);
perPointPattern = perPointPattern || d.mcc ||
Lib.isArrayOrTypedArray(markerPattern.shape) ||
Lib.isArrayOrTypedArray(markerPattern.path) ||
Lib.isArrayOrTypedArray(markerPattern.bgcolor) ||
Lib.isArrayOrTypedArray(markerPattern.fgcolor) ||
Lib.isArrayOrTypedArray(markerPattern.size) ||
Expand Down
9 changes: 6 additions & 3 deletions src/components/legend/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,14 @@ module.exports = function style(s, gd, legend) {
var fillColor = mcc || d0.mc || marker.color;

var markerPattern = marker.pattern;
var patternShape = markerPattern && Drawing.getPatternAttr(markerPattern.shape, 0, '');
var pAttr = Drawing.getPatternAttr;
var patternShape = markerPattern && (
pAttr(markerPattern.shape, 0, '') || pAttr(markerPattern.path, 0, '')
);

if(patternShape) {
var patternBGColor = Drawing.getPatternAttr(markerPattern.bgcolor, 0, null);
var patternFGColor = Drawing.getPatternAttr(markerPattern.fgcolor, 0, null);
var patternBGColor = pAttr(markerPattern.bgcolor, 0, null);
var patternFGColor = pAttr(markerPattern.fgcolor, 0, null);
var patternFGOpacity = markerPattern.fgopacity;
var patternSize = dimAttr(markerPattern.size, 8, 10);
var patternSolidity = dimAttr(markerPattern.solidity, 0.5, 1);
Expand Down
10 changes: 8 additions & 2 deletions src/lib/coerce.js
Original file line number Diff line number Diff line change
Expand Up @@ -502,8 +502,14 @@ exports.coerceFont = function(coerce, attr, dfltObj, opts) {
*/
exports.coercePattern = function(coerce, attr, markerColor, hasMarkerColorscale) {
var shape = coerce(attr + '.shape');
if(shape) {
coerce(attr + '.solidity');
var path;
if(!shape) {
path = coerce(attr + '.path');
}
if(shape || path) {
if(shape) {
coerce(attr + '.solidity');
}
coerce(attr + '.size');
var fillmode = coerce(attr + '.fillmode');
var isOverlay = fillmode === 'overlay';
Expand Down
Binary file modified test/image/baselines/pattern_bars.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 21 additions & 1 deletion test/image/mocks/pattern_bars.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,27 @@
}
}
},

{
"x": ["a", "b", "c", "d", "e"],
"y": [7, 8, 9, 10, 11],
"name": "Bar 5",
"type": "bar",
"textposition": "outside",
"text": "path",
"marker": {
"pattern": {
"path": [
"M0,0H4V4H0Z",
"M0,0H6V6Z",
"M0,0V4H4Z",
"M0,0C0,2,4,2,4,4C4,6,0,6,0,8H2C2,6,6,6,6,4C6,2,2,2,2,0Z",
"M4,4L7,2A3.5,3.5,0,1,0,7,6Z"
],
"fgcolor": "yellow",
"bgcolor": "black"
}
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to keep pattern_bars.json as it was before.
Please duplicate the mock and name the new mock zz-pattern_bars-path.json.

This is wonderful feature.
Thanks @alexcjohnson for the PR. 🏆

We may introduce new features like this one in v3.1.0 not v3.0.0; even though one may argue that it's a low risk feature. BTW it requires extra testing on the python side, etc.
cc: @gvwilson

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK - personally I think the mocks are getting a little excessive, and we'd be better off testing as many things as we can in one mock, which is why I just added this to one of the existing mocks. But I've made the change.

{
"t": ["M", "N", "O", "P"],
"r": [1, 2, 3, 4],
Expand Down
99 changes: 99 additions & 0 deletions test/plot-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -17808,6 +17808,17 @@
"overlay"
]
},
"path": {
"arrayOk": true,
"description": "Sets a custom path for pattern fill. Use with no `shape` or `solidity`, provide an SVG path string for the regions of the square from (0,0) to (`size`,`size`) to color.",
"editType": "style",
"valType": "string"
},
"pathsrc": {
"description": "Sets the source reference on Chart Studio Cloud for `path`.",
"editType": "none",
"valType": "string"
},
"role": "object",
"shape": {
"arrayOk": true,
Expand Down Expand Up @@ -19835,6 +19846,17 @@
"overlay"
]
},
"path": {
"arrayOk": true,
"description": "Sets a custom path for pattern fill. Use with no `shape` or `solidity`, provide an SVG path string for the regions of the square from (0,0) to (`size`,`size`) to color.",
"editType": "style",
"valType": "string"
},
"pathsrc": {
"description": "Sets the source reference on Chart Studio Cloud for `path`.",
"editType": "none",
"valType": "string"
},
"role": "object",
"shape": {
"arrayOk": true,
Expand Down Expand Up @@ -37562,6 +37584,17 @@
"overlay"
]
},
"path": {
"arrayOk": true,
"description": "Sets a custom path for pattern fill. Use with no `shape` or `solidity`, provide an SVG path string for the regions of the square from (0,0) to (`size`,`size`) to color.",
"editType": "style",
"valType": "string"
},
"pathsrc": {
"description": "Sets the source reference on Chart Studio Cloud for `path`.",
"editType": "none",
"valType": "string"
},
"role": "object",
"shape": {
"arrayOk": true,
Expand Down Expand Up @@ -41225,6 +41258,17 @@
"overlay"
]
},
"path": {
"arrayOk": true,
"description": "Sets a custom path for pattern fill. Use with no `shape` or `solidity`, provide an SVG path string for the regions of the square from (0,0) to (`size`,`size`) to color.",
"editType": "style",
"valType": "string"
},
"pathsrc": {
"description": "Sets the source reference on Chart Studio Cloud for `path`.",
"editType": "none",
"valType": "string"
},
"role": "object",
"shape": {
"arrayOk": true,
Expand Down Expand Up @@ -46456,6 +46500,17 @@
"overlay"
]
},
"path": {
"arrayOk": true,
"description": "Sets a custom path for pattern fill. Use with no `shape` or `solidity`, provide an SVG path string for the regions of the square from (0,0) to (`size`,`size`) to color.",
"editType": "style",
"valType": "string"
},
"pathsrc": {
"description": "Sets the source reference on Chart Studio Cloud for `path`.",
"editType": "none",
"valType": "string"
},
"role": "object",
"shape": {
"arrayOk": true,
Expand Down Expand Up @@ -56226,6 +56281,17 @@
"overlay"
]
},
"path": {
"arrayOk": true,
"description": "Sets a custom path for pattern fill. Use with no `shape` or `solidity`, provide an SVG path string for the regions of the square from (0,0) to (`size`,`size`) to color.",
"editType": "style",
"valType": "string"
},
"pathsrc": {
"description": "Sets the source reference on Chart Studio Cloud for `path`.",
"editType": "none",
"valType": "string"
},
"role": "object",
"shape": {
"arrayOk": true,
Expand Down Expand Up @@ -58612,6 +58678,17 @@
"overlay"
]
},
"path": {
"arrayOk": true,
"description": "Sets a custom path for pattern fill. Use with no `shape` or `solidity`, provide an SVG path string for the regions of the square from (0,0) to (`size`,`size`) to color.",
"editType": "style",
"valType": "string"
},
"pathsrc": {
"description": "Sets the source reference on Chart Studio Cloud for `path`.",
"editType": "none",
"valType": "string"
},
"role": "object",
"shape": {
"arrayOk": true,
Expand Down Expand Up @@ -87705,6 +87782,17 @@
"overlay"
]
},
"path": {
"arrayOk": true,
"description": "Sets a custom path for pattern fill. Use with no `shape` or `solidity`, provide an SVG path string for the regions of the square from (0,0) to (`size`,`size`) to color.",
"editType": "style",
"valType": "string"
},
"pathsrc": {
"description": "Sets the source reference on Chart Studio Cloud for `path`.",
"editType": "none",
"valType": "string"
},
"role": "object",
"shape": {
"arrayOk": true,
Expand Down Expand Up @@ -92346,6 +92434,17 @@
"overlay"
]
},
"path": {
"arrayOk": true,
"description": "Sets a custom path for pattern fill. Use with no `shape` or `solidity`, provide an SVG path string for the regions of the square from (0,0) to (`size`,`size`) to color.",
"editType": "style",
"valType": "string"
},
"pathsrc": {
"description": "Sets the source reference on Chart Studio Cloud for `path`.",
"editType": "none",
"valType": "string"
},
"role": "object",
"shape": {
"arrayOk": true,
Expand Down