diff --git a/draftlogs/7005_add.md b/draftlogs/7005_add.md new file mode 100644 index 00000000000..af079090b96 --- /dev/null +++ b/draftlogs/7005_add.md @@ -0,0 +1 @@ + - Add property x0shift, x1shift, y0shift, y1shift for shapes referencing (multi-)category axes, with thanks to @my-tien for the contribution! [[#7005](https://github.com/plotly/plotly.js/pull/7005)] diff --git a/src/components/shapes/attributes.js b/src/components/shapes/attributes.js index 6a6e5a3a846..71a5475aee0 100644 --- a/src/components/shapes/attributes.js +++ b/src/components/shapes/attributes.js @@ -169,7 +169,30 @@ module.exports = templatedArray('shape', { 'See `type` and `xsizemode` for more info.' ].join(' ') }, - + x0shift: { + valType: 'number', + dflt: 0, + min: -1, + max: 1, + editType: 'calc', + description: [ + 'Shifts `x0` away from the center of the category when `xref` is a *category* or', + '*multicategory* axis. -0.5 corresponds to the start of the category and 0.5', + 'corresponds to the end of the category.' + ].join(' ') + }, + x1shift: { + valType: 'number', + dflt: 0, + min: -1, + max: 1, + editType: 'calc', + description: [ + 'Shifts `x1` away from the center of the category when `xref` is a *category* or', + '*multicategory* axis. -0.5 corresponds to the start of the category and 0.5', + 'corresponds to the end of the category.' + ].join(' ') + }, yref: extendFlat({}, annAttrs.yref, { description: [ 'Sets the shape\'s y coordinate axis.', @@ -220,7 +243,30 @@ module.exports = templatedArray('shape', { 'See `type` and `ysizemode` for more info.' ].join(' ') }, - + y0shift: { + valType: 'number', + dflt: 0, + min: -1, + max: 1, + editType: 'calc', + description: [ + 'Shifts `y0` away from the center of the category when `yref` is a *category* or', + '*multicategory* axis. -0.5 corresponds to the start of the category and 0.5', + 'corresponds to the end of the category.' + ].join(' ') + }, + y1shift: { + valType: 'number', + dflt: 0, + min: -1, + max: 1, + editType: 'calc', + description: [ + 'Shifts `y1` away from the center of the category when `yref` is a *category* or', + '*multicategory* axis. -0.5 corresponds to the start of the category and 0.5', + 'corresponds to the end of the category.' + ].join(' ') + }, path: { valType: 'string', editType: 'calc+arraydraw', diff --git a/src/components/shapes/calc_autorange.js b/src/components/shapes/calc_autorange.js index 7892bceb12f..17c6ce23a2f 100644 --- a/src/components/shapes/calc_autorange.js +++ b/src/components/shapes/calc_autorange.js @@ -23,22 +23,18 @@ module.exports = function calcAutorange(gd) { // paper and axis domain referenced shapes don't affect autorange if(shape.xref !== 'paper' && xRefType !== 'domain') { - var vx0 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x0; - var vx1 = shape.xsizemode === 'pixel' ? shape.xanchor : shape.x1; ax = Axes.getFromId(gd, shape.xref); - bounds = shapeBounds(ax, vx0, vx1, shape.path, constants.paramIsX); + bounds = shapeBounds(ax, shape, constants.paramIsX); if(bounds) { shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcXPaddingOptions(shape)); } } if(shape.yref !== 'paper' && yRefType !== 'domain') { - var vy0 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y0; - var vy1 = shape.ysizemode === 'pixel' ? shape.yanchor : shape.y1; ax = Axes.getFromId(gd, shape.yref); - bounds = shapeBounds(ax, vy0, vy1, shape.path, constants.paramIsY); + bounds = shapeBounds(ax, shape, constants.paramIsY); if(bounds) { shape._extremes[ax._id] = Axes.findExtremes(ax, bounds, calcYPaddingOptions(shape)); } @@ -77,15 +73,35 @@ function calcPaddingOptions(lineWidth, sizeMode, v0, v1, path, isYAxis) { } } -function shapeBounds(ax, v0, v1, path, paramsToUse) { - var convertVal = (ax.type === 'category' || ax.type === 'multicategory') ? ax.r2c : ax.d2c; +function shapeBounds(ax, shape, paramsToUse) { + var dim = ax._id.charAt(0) === 'x' ? 'x' : 'y'; + var isCategory = ax.type === 'category' || ax.type === 'multicategory'; + var v0; + var v1; + var shiftStart = 0; + var shiftEnd = 0; + + var convertVal = isCategory ? ax.r2c : ax.d2c; + + var isSizeModeScale = shape[dim + 'sizemode'] === 'scaled'; + if(isSizeModeScale) { + v0 = shape[dim + '0']; + v1 = shape[dim + '1']; + if(isCategory) { + shiftStart = shape[dim + '0shift']; + shiftEnd = shape[dim + '1shift']; + } + } else { + v0 = shape[dim + 'anchor']; + v1 = shape[dim + 'anchor']; + } - if(v0 !== undefined) return [convertVal(v0), convertVal(v1)]; - if(!path) return; + if(v0 !== undefined) return [convertVal(v0) + shiftStart, convertVal(v1) + shiftEnd]; + if(!shape.path) return; var min = Infinity; var max = -Infinity; - var segments = path.match(constants.segmentRE); + var segments = shape.path.match(constants.segmentRE); var i; var segment; var drawnParam; diff --git a/src/components/shapes/defaults.js b/src/components/shapes/defaults.js index 3f52437b654..e725b336678 100644 --- a/src/components/shapes/defaults.js +++ b/src/components/shapes/defaults.js @@ -85,6 +85,10 @@ function handleShapeDefaults(shapeIn, shapeOut, fullLayout) { ax._shapeIndices.push(shapeOut._index); r2pos = helpers.rangeToShapePosition(ax); pos2r = helpers.shapePositionToRange(ax); + if(ax.type === 'category' || ax.type === 'multicategory') { + coerce(axLetter + '0shift'); + coerce(axLetter + '1shift'); + } } else { pos2r = r2pos = Lib.identity; } diff --git a/src/components/shapes/display_labels.js b/src/components/shapes/display_labels.js index 82c0856d943..53c9d96228b 100644 --- a/src/components/shapes/display_labels.js +++ b/src/components/shapes/display_labels.js @@ -88,15 +88,25 @@ module.exports = function drawLabel(gd, index, options, shapeGroup) { // and convert them to pixel coordinates // Setup conversion functions var xa = Axes.getFromId(gd, options.xref); + var xShiftStart = options.x0shift; + var xShiftEnd = options.x1shift; var xRefType = Axes.getRefType(options.xref); var ya = Axes.getFromId(gd, options.yref); + var yShiftStart = options.y0shift; + var yShiftEnd = options.y1shift; var yRefType = Axes.getRefType(options.yref); - var x2p = helpers.getDataToPixel(gd, xa, false, xRefType); - var y2p = helpers.getDataToPixel(gd, ya, true, yRefType); - shapex0 = x2p(options.x0); - shapex1 = x2p(options.x1); - shapey0 = y2p(options.y0); - shapey1 = y2p(options.y1); + var x2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, xa, shift, false, xRefType); + return dataToPixel(v); + }; + var y2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, ya, shift, true, yRefType); + return dataToPixel(v); + }; + shapex0 = x2p(options.x0, xShiftStart); + shapex1 = x2p(options.x1, xShiftEnd); + shapey0 = y2p(options.y0, yShiftStart); + shapey1 = y2p(options.y1, yShiftEnd); } // Handle `auto` angle diff --git a/src/components/shapes/draw.js b/src/components/shapes/draw.js index 417c884c3f2..4cc75dc920e 100644 --- a/src/components/shapes/draw.js +++ b/src/components/shapes/draw.js @@ -227,8 +227,18 @@ function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHe var xRefType = Axes.getRefType(shapeOptions.xref); var ya = Axes.getFromId(gd, shapeOptions.yref); var yRefType = Axes.getRefType(shapeOptions.yref); - var x2p = helpers.getDataToPixel(gd, xa, false, xRefType); - var y2p = helpers.getDataToPixel(gd, ya, true, yRefType); + var shiftXStart = shapeOptions.x0shift; + var shiftXEnd = shapeOptions.x1shift; + var shiftYStart = shapeOptions.y0shift; + var shiftYEnd = shapeOptions.y1shift; + var x2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, xa, shift, false, xRefType); + return dataToPixel(v); + }; + var y2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, ya, shift, true, yRefType); + return dataToPixel(v); + }; var p2x = helpers.getPixelToData(gd, xa, false, xRefType); var p2y = helpers.getPixelToData(gd, ya, true, yRefType); @@ -279,8 +289,8 @@ function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHe g.append('circle') .attr({ 'data-line-point': 'start-point', - cx: xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x0 : x2p(shapeOptions.x0), - cy: yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y0 : y2p(shapeOptions.y0), + cx: xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x0 : x2p(shapeOptions.x0, shiftXStart), + cy: yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y0 : y2p(shapeOptions.y0, shiftYStart), r: circleRadius }) .style(circleStyle) @@ -289,8 +299,8 @@ function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHe g.append('circle') .attr({ 'data-line-point': 'end-point', - cx: xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x1 : x2p(shapeOptions.x1), - cy: yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y1 : y2p(shapeOptions.y1), + cx: xPixelSized ? x2p(shapeOptions.xanchor) + shapeOptions.x1 : x2p(shapeOptions.x1, shiftXEnd), + cy: yPixelSized ? y2p(shapeOptions.yanchor) - shapeOptions.y1 : y2p(shapeOptions.y1, shiftYEnd), r: circleRadius }) .style(circleStyle) diff --git a/src/components/shapes/draw_newshape/newshapes.js b/src/components/shapes/draw_newshape/newshapes.js index 9db9a237a4d..831a270e9ab 100644 --- a/src/components/shapes/draw_newshape/newshapes.js +++ b/src/components/shapes/draw_newshape/newshapes.js @@ -84,10 +84,10 @@ function newShapes(outlines, dragOptions) { case 'line': case 'rect': case 'circle': - modifyItem('x0', afterEdit.x0); - modifyItem('x1', afterEdit.x1); - modifyItem('y0', afterEdit.y0); - modifyItem('y1', afterEdit.y1); + modifyItem('x0', afterEdit.x0 - (beforeEdit.x0shift || 0)); + modifyItem('x1', afterEdit.x1 - (beforeEdit.x1shift || 0)); + modifyItem('y0', afterEdit.y0 - (beforeEdit.y0shift || 0)); + modifyItem('y1', afterEdit.y1 - (beforeEdit.y1shift || 0)); break; case 'path': diff --git a/src/components/shapes/helpers.js b/src/components/shapes/helpers.js index d07fe6948eb..ad22a48df8c 100644 --- a/src/components/shapes/helpers.js +++ b/src/components/shapes/helpers.js @@ -53,7 +53,7 @@ exports.extractPathCoords = function(path, paramsToUse, isRaw) { return extractedCoordinates; }; -exports.getDataToPixel = function(gd, axis, isVertical, refType) { +exports.getDataToPixel = function(gd, axis, shift, isVertical, refType) { var gs = gd._fullLayout._size; var dataToPixel; @@ -66,7 +66,8 @@ exports.getDataToPixel = function(gd, axis, isVertical, refType) { var d2r = exports.shapePositionToRange(axis); dataToPixel = function(v) { - return axis._offset + axis.r2p(d2r(v, true)); + var shiftPixels = getPixelShift(axis, shift); + return axis._offset + axis.r2p(d2r(v, true)) + shiftPixels; }; if(axis.type === 'date') dataToPixel = exports.decodeDate(dataToPixel); @@ -179,6 +180,10 @@ exports.getPathString = function(gd, options) { var ya = Axes.getFromId(gd, options.yref); var gs = gd._fullLayout._size; var x2r, x2p, y2r, y2p; + var xShiftStart = getPixelShift(xa, options.x0shift); + var xShiftEnd = getPixelShift(xa, options.x1shift); + var yShiftStart = getPixelShift(ya, options.y0shift); + var yShiftEnd = getPixelShift(ya, options.y1shift); var x0, x1, y0, y1; if(xa) { @@ -208,23 +213,22 @@ exports.getPathString = function(gd, options) { if(ya && ya.type === 'date') y2p = exports.decodeDate(y2p); return convertPath(options, x2p, y2p); } - if(options.xsizemode === 'pixel') { var xAnchorPos = x2p(options.xanchor); - x0 = xAnchorPos + options.x0; - x1 = xAnchorPos + options.x1; + x0 = xAnchorPos + options.x0 + xShiftStart; + x1 = xAnchorPos + options.x1 + xShiftEnd; } else { - x0 = x2p(options.x0); - x1 = x2p(options.x1); + x0 = x2p(options.x0) + xShiftStart; + x1 = x2p(options.x1) + xShiftEnd; } if(options.ysizemode === 'pixel') { var yAnchorPos = y2p(options.yanchor); - y0 = yAnchorPos - options.y0; - y1 = yAnchorPos - options.y1; + y0 = yAnchorPos - options.y0 + yShiftStart; + y1 = yAnchorPos - options.y1 + yShiftEnd; } else { - y0 = y2p(options.y0); - y1 = y2p(options.y1); + y0 = y2p(options.y0) + yShiftStart; + y1 = y2p(options.y1) + yShiftEnd; } if(type === 'line') return 'M' + x0 + ',' + y0 + 'L' + x1 + ',' + y1; @@ -279,3 +283,12 @@ function convertPath(options, x2p, y2p) { return segmentType + paramString; }); } + +function getPixelShift(axis, shift) { + shift = shift || 0; + var shiftPixels = 0; + if(shift && axis && (axis.type === 'category' || axis.type === 'multicategory')) { + shiftPixels = (axis.r2p(1) - axis.r2p(0)) * shift; + } + return shiftPixels; +} diff --git a/src/components/shapes/label_texttemplate.js b/src/components/shapes/label_texttemplate.js index 47328f89d00..445a8541247 100644 --- a/src/components/shapes/label_texttemplate.js +++ b/src/components/shapes/label_texttemplate.js @@ -16,12 +16,17 @@ function x1Fn(shape) { return shape.x1; } function y0Fn(shape) { return shape.y0; } function y1Fn(shape) { return shape.y1; } +function x0shiftFn(shape) { return shape.x0shift || 0; } +function x1shiftFn(shape) { return shape.x1shift || 0; } +function y0shiftFn(shape) { return shape.y0shift || 0; } +function y1shiftFn(shape) { return shape.y1shift || 0; } + function dxFn(shape, xa) { - return d2l(shape.x1, xa) - d2l(shape.x0, xa); + return d2l(shape.x1, xa) + x1shiftFn(shape) - d2l(shape.x0, xa) - x0shiftFn(shape); } function dyFn(shape, xa, ya) { - return d2l(shape.y1, ya) - d2l(shape.y0, ya); + return d2l(shape.y1, ya) + y1shiftFn(shape) - d2l(shape.y0, ya) - y0shiftFn(shape); } function widthFn(shape, xa) { @@ -41,11 +46,11 @@ function lengthFn(shape, xa, ya) { } function xcenterFn(shape, xa) { - return l2d((d2l(shape.x1, xa) + d2l(shape.x0, xa)) / 2, xa); + return l2d((d2l(shape.x1, xa) + x1shiftFn(shape) + d2l(shape.x0, xa) + x0shiftFn(shape)) / 2, xa); } function ycenterFn(shape, xa, ya) { - return l2d((d2l(shape.y1, ya) + d2l(shape.y0, ya)) / 2, ya); + return l2d((d2l(shape.y1, ya) + y1shiftFn(shape) + d2l(shape.y0, ya) + y0shiftFn(shape)) / 2, ya); } function slopeFn(shape, xa, ya) { diff --git a/test/image/baselines/zzz_shape_shift_horizontal.png b/test/image/baselines/zzz_shape_shift_horizontal.png new file mode 100644 index 00000000000..427911e789b Binary files /dev/null and b/test/image/baselines/zzz_shape_shift_horizontal.png differ diff --git a/test/image/baselines/zzz_shape_shift_vertical.png b/test/image/baselines/zzz_shape_shift_vertical.png new file mode 100644 index 00000000000..9e5bc4162c9 Binary files /dev/null and b/test/image/baselines/zzz_shape_shift_vertical.png differ diff --git a/test/image/mocks/zzz_shape_shift_horizontal.json b/test/image/mocks/zzz_shape_shift_horizontal.json new file mode 100644 index 00000000000..2ce25466211 --- /dev/null +++ b/test/image/mocks/zzz_shape_shift_horizontal.json @@ -0,0 +1,89 @@ +{ + "data": [ + { + "y": [ + ["A", "A", "B", "B"], ["C", "D", "C", "D"] + ], + "x": [ + 1, 2, 3, 4 + ], + "type": "bar", + "marker": { + "color": "rgba(153, 217, 234, 1)" + }, + "orientation": "h" + }, + { + "y": [ + ["A", "A", "B", "B"], ["C", "D", "C", "D"] + ], + "x": [ + 4, 3, 2, 1 + ], + "type": "bar", + "orientation": "h" + } + ], + "layout": { + "shapes": [ + { + "layer": "above", + "type": "rect", + "label": { + "text": "around A,D", + "textposition": "bottom center", + "textangle": 0 + }, + "line": { + "color": "black", + "width": 3.0 + }, + "y0": ["A", "D"], + "y1": ["A", "D"], + "x0": 0, + "x1": 0.25, + "y0shift": -0.5, + "y1shift": 0.5, + "xref": "paper" + }, + { + "layer": "above", + "type": "line", + "label": { + "text": "on B,D", + "textposition": "middle", + "textangle": 0 + }, + "line": { + "color": "blue", + "width": 3.0 + }, + "y0": ["B", "D"], + "y1": ["B", "D"], + "x0": 0, + "x1": 0.25, + "xref": "paper" + }, + { + "layer": "above", + "type": "line", + "label": { + "text": "Before B,D", + "textposition": "middle", + "textangle": 0 + }, + "line": { + "color": "green", + "width": 3.0 + }, + "y0": ["B", "D"], + "y1": ["B", "D"], + "x0": 0, + "x1": 0.25, + "y0shift": -0.5, + "y1shift": -0.5, + "xref": "paper" + } + ] + } +} diff --git a/test/image/mocks/zzz_shape_shift_vertical.json b/test/image/mocks/zzz_shape_shift_vertical.json new file mode 100644 index 00000000000..46715c8175b --- /dev/null +++ b/test/image/mocks/zzz_shape_shift_vertical.json @@ -0,0 +1,92 @@ +{ + "data": [ + { + "x": [ + "A", "B", "C", "D" + ], + "y": [ + 1, 2, 2.5, 4 + ], + "type": "bar" + }, + { + "x": [ + "A", "B", "C", "D" + ], + "y": [ + 3, 2, 1.5, 1 + ], + "type": "scatter" + } + ], + "layout": { + "width": 600, + "xaxis": { + "autorange": "reversed" + }, + "yaxis": { + "range": [0, 5] + }, + "shapes": [ + { + "layer": "above", + "type": "line", + "label": { + "text": "after A", + "textposition": "end", + "textangle": 0 + }, + "line": { + "color": "yellow", + "width": 3.0 + }, + "x0": "A", + "x1": "A", + "y0": 0, + "y1": 0.5, + "x0shift": 0.5, + "x1shift": 0.5, + "yref": "paper" + }, + { + "layer": "above", + "type": "line", + "label": { + "text": "slightly before D", + "textposition": "end", + "textangle": 0 + }, + "line": { + "color": "pink", + "width": 3.0 + }, + "x0": 3, + "x1": 3, + "y0": 0, + "y1": 0.5, + "x0shift": -0.25, + "x1shift": -0.25, + "yref": "paper" + }, + { + "label": { + "texttemplate": "dy/dx = -1.5 / 1 = %{slope}. xcenter: %{xcenter}" + + }, + "layer": "above", + "x0": "A", + "x1": "C", + "x0shift": 0.5, + "x1shift": -0.5, + "y0": 4.5, + "y1": 3, + "type": "line", + "line": { + "width": 3, + "color": "pink" + }, + "editable": true + } + ] + } +} diff --git a/test/jasmine/tests/shapes_test.js b/test/jasmine/tests/shapes_test.js index 112add2bee7..1ea56af23ee 100644 --- a/test/jasmine/tests/shapes_test.js +++ b/test/jasmine/tests/shapes_test.js @@ -1487,8 +1487,14 @@ describe('Test shapes', function() { function testShapeDrag(dx, dy, layoutShape, node) { var xa = Axes.getFromId(gd, layoutShape.xref); var ya = Axes.getFromId(gd, layoutShape.yref); - var x2p = helpers.getDataToPixel(gd, xa); - var y2p = helpers.getDataToPixel(gd, ya, true); + var x2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, xa, shift); + return dataToPixel(v); + }; + var y2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, ya, shift, true); + return dataToPixel(v); + }; var initialCoordinates = getShapeCoordinates(layoutShape, x2p, y2p); @@ -1504,10 +1510,10 @@ describe('Test shapes', function() { function getShapeCoordinates(layoutShape, x2p, y2p) { return { - x0: x2p(layoutShape.x0), - x1: x2p(layoutShape.x1), - y0: y2p(layoutShape.y0), - y1: y2p(layoutShape.y1) + x0: x2p(layoutShape.x0, layoutShape.x0shift), + x1: x2p(layoutShape.x1, layoutShape.x1shift), + y0: y2p(layoutShape.y0, layoutShape.y0shift), + y1: y2p(layoutShape.y1, layoutShape.y1shift) }; } @@ -1515,7 +1521,7 @@ describe('Test shapes', function() { var xa = Axes.getFromId(gd, layoutShape.xref); var ya = Axes.getFromId(gd, layoutShape.yref); var x2p = helpers.getDataToPixel(gd, xa); - var y2p = helpers.getDataToPixel(gd, ya, true); + var y2p = helpers.getDataToPixel(gd, ya, undefined, true); var initialPath = layoutShape.path; var initialCoordinates = getPathCoordinates(initialPath, x2p, y2p); @@ -1546,8 +1552,14 @@ describe('Test shapes', function() { function testShapeResize(direction, dx, dy, layoutShape, node) { var xa = Axes.getFromId(gd, layoutShape.xref); var ya = Axes.getFromId(gd, layoutShape.yref); - var x2p = helpers.getDataToPixel(gd, xa); - var y2p = helpers.getDataToPixel(gd, ya, true); + var x2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, xa, shift, false); + return dataToPixel(v); + }; + var y2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, ya, shift, true); + return dataToPixel(v); + }; var initialCoordinates = getShapeCoordinates(layoutShape, x2p, y2p); @@ -1590,9 +1602,14 @@ describe('Test shapes', function() { var xa = Axes.getFromId(gd, layoutShape.xref); var ya = Axes.getFromId(gd, layoutShape.yref); - var x2p = helpers.getDataToPixel(gd, xa); - var y2p = helpers.getDataToPixel(gd, ya, true); - + var x2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, xa, shift); + return dataToPixel(v); + }; + var y2p = function(v, shift) { + var dataToPixel = helpers.getDataToPixel(gd, ya, shift, true); + return dataToPixel(v); + }; promise = promise.then(function() { var dragHandle = pointToMove === 'start' ? diff --git a/test/plot-schema.json b/test/plot-schema.json index 718522bcd18..b414b47a808 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -10015,11 +10015,27 @@ "editType": "calc+arraydraw", "valType": "any" }, + "x0shift": { + "description": "Shifts `x0` away from the center of the category when `xref` is a *category* or *multicategory* axis. -0.5 corresponds to the start of the category and 0.5 corresponds to the end of the category.", + "dflt": 0, + "editType": "calc", + "max": 1, + "min": -1, + "valType": "number" + }, "x1": { "description": "Sets the shape's end x position. See `type` and `xsizemode` for more info.", "editType": "calc+arraydraw", "valType": "any" }, + "x1shift": { + "description": "Shifts `x1` away from the center of the category when `xref` is a *category* or *multicategory* axis. -0.5 corresponds to the start of the category and 0.5 corresponds to the end of the category.", + "dflt": 0, + "editType": "calc", + "max": 1, + "min": -1, + "valType": "number" + }, "xanchor": { "description": "Only relevant in conjunction with `xsizemode` set to *pixel*. Specifies the anchor point on the x axis to which `x0`, `x1` and x coordinates within `path` are relative to. E.g. useful to attach a pixel sized shape to a certain data value. No effect when `xsizemode` not set to *pixel*.", "editType": "calc+arraydraw", @@ -10049,11 +10065,27 @@ "editType": "calc+arraydraw", "valType": "any" }, + "y0shift": { + "description": "Shifts `y0` away from the center of the category when `yref` is a *category* or *multicategory* axis. -0.5 corresponds to the start of the category and 0.5 corresponds to the end of the category.", + "dflt": 0, + "editType": "calc", + "max": 1, + "min": -1, + "valType": "number" + }, "y1": { "description": "Sets the shape's end y position. See `type` and `ysizemode` for more info.", "editType": "calc+arraydraw", "valType": "any" }, + "y1shift": { + "description": "Shifts `y1` away from the center of the category when `yref` is a *category* or *multicategory* axis. -0.5 corresponds to the start of the category and 0.5 corresponds to the end of the category.", + "dflt": 0, + "editType": "calc", + "max": 1, + "min": -1, + "valType": "number" + }, "yanchor": { "description": "Only relevant in conjunction with `ysizemode` set to *pixel*. Specifies the anchor point on the y axis to which `y0`, `y1` and y coordinates within `path` are relative to. E.g. useful to attach a pixel sized shape to a certain data value. No effect when `ysizemode` not set to *pixel*.", "editType": "calc+arraydraw",