diff --git a/draftlogs/7280_add.md b/draftlogs/7280_add.md new file mode 100644 index 00000000000..b4a3ecd69d7 --- /dev/null +++ b/draftlogs/7280_add.md @@ -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)] diff --git a/src/components/drawing/attributes.js b/src/components/drawing/attributes.js index 0d3447c620d..db725070456 100644 --- a/src/components/drawing/attributes.js +++ b/src/components/drawing/attributes.js @@ -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'], diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index 5fa39480d6e..3cb3b447eeb 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -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, @@ -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 = [ @@ -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; @@ -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) || diff --git a/src/components/legend/style.js b/src/components/legend/style.js index f1400698d8b..849271d3962 100644 --- a/src/components/legend/style.js +++ b/src/components/legend/style.js @@ -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); diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 7e879b59992..9fa59255bc1 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -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'; diff --git a/src/plot_api/validate.js b/src/plot_api/validate.js index 45be7d7bbc4..8119eb4a627 100644 --- a/src/plot_api/validate.js +++ b/src/plot_api/validate.js @@ -214,7 +214,11 @@ function crawl(objIn, objOut, schema, list, base, path) { } else if(!Lib.validate(valIn, nestedSchema)) { list.push(format('value', base, p, valIn)); } else if(nestedSchema.valType === 'enumerated' && - ((nestedSchema.coerceNumber && valIn !== +valOut) || valIn !== valOut) + ( + (nestedSchema.coerceNumber && valIn !== +valOut) || + (!isArrayOrTypedArray(valIn) && valIn !== valOut) || + (String(valIn) !== String(valOut)) + ) ) { list.push(format('dynamic', base, p, valIn, valOut)); } diff --git a/test/image/baselines/zz-pattern_bars-path.png b/test/image/baselines/zz-pattern_bars-path.png new file mode 100644 index 00000000000..cff51494b5b Binary files /dev/null and b/test/image/baselines/zz-pattern_bars-path.png differ diff --git a/test/image/mocks/zz-pattern_bars-path.json b/test/image/mocks/zz-pattern_bars-path.json new file mode 100644 index 00000000000..13e554ed4c3 --- /dev/null +++ b/test/image/mocks/zz-pattern_bars-path.json @@ -0,0 +1,218 @@ +{ + "data": [ + { + "x": ["a", "b", "c", "d", "e"], + "y": [1, 2, 3, 4, 5], + "name": "Bar 1", + "type": "bar", + "textposition": "outside", + "text": "bgcolor", + "marker": { + "pattern": { + "shape": "/", + "bgcolor": ["", "lightblue", "blue", "darkblue", "black"] + } + } + }, + { + "x": ["a", "b", "c", "d", "e"], + "y": [2, 3, 4, 5, 6], + "name": "Bar 2", + "type": "bar", + "textposition": "outside", + "text": "shape", + "marker": { + "pattern": { + "shape": ["|", "/", "-", "\\", "|"] + } + } + }, + { + "x": ["a", "b", "c", "d", "e"], + "y": [3, 4, 5, 6, 7], + "name": "Bar 3", + "type": "bar", + "textposition": "outside", + "text": "size", + "marker": { + "pattern": { + "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": { + "shape": ".", + "bgcolor": "yellow", + "solidity": [0.1, 0.3, 0.5, 0.7, 0.9] + } + } + }, + { + "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" + } + } + }, + { + "r": [1, 2, 3, 4], + "type": "barpolar", + "name": "Barpolar 1", + "marker": { + "color": "red", + "pattern": { + "shape": "+", + "size": [1, 2, 3, 4] + } + } + }, + { + "r": [2, 3, 4, 1], + "type": "barpolar", + "name": "Barpolar 2", + "marker": { + "color": "rgba(0,127,0,0.5)", + "pattern": { + "shape": "x", + "solidity": 0.75 + } + } + }, + { + "r": [3, 4, 1, 2], + "type": "barpolar", + "name": "Barpolar 3", + "marker": { + "color": "blue", + "pattern": { + "shape": ["|", "-", "|", "-"], + "solidity": 0.5 + } + } + }, + { + "r": [4, 1, 2, 3], + "type": "barpolar", + "name": "Barpolar 4", + "marker": { + "color": "orange", + "pattern": { + "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": { + "bgcolor": "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": { + "bgcolor": "rgba(255, 127,0,0.5)", + "shape": "x" + } + } + }, + + { + "xaxis": "x3", + "yaxis": "y3", + "x": [3, 2, 1], + "y": ["U", "V", "W"], + "name": "Funnel", + "type": "funnel" + } + ], + "layout": { + "title": { + "text": "pattern options" + }, + "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/plot-schema.json b/test/plot-schema.json index 6aa77cf3338..dc18446a37e 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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,
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: