Skip to content
Draft
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
20 changes: 14 additions & 6 deletions src/components/shapes/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,16 @@ module.exports = templatedArray('shape', {
].join(' ')
},

xref: extendFlat({}, annAttrs.xref, {
xref: {
valType: 'any',
editType: 'calc',
description: [
'Sets the shape\'s x coordinate axis.',
axisPlaceableObjs.axisRefDescription('x', 'left', 'right')
axisPlaceableObjs.axisRefDescription('x', 'left', 'right'),
'If an array of axis IDs is provided, each `x` value will refer to the corresponding axis',
'(e.g., [\'x\', \'x2\'] for a rectangle means `x0` uses the `x` axis and `x1` uses the `x2` axis).',
].join(' ')
}),
},
xsizemode: {
valType: 'enumerated',
values: ['scaled', 'pixel'],
Expand Down Expand Up @@ -193,12 +197,16 @@ module.exports = templatedArray('shape', {
'corresponds to the end of the category.'
].join(' ')
},
yref: extendFlat({}, annAttrs.yref, {
yref: {
valType: 'any',
editType: 'calc',
description: [
'Sets the shape\'s y coordinate axis.',
axisPlaceableObjs.axisRefDescription('y', 'bottom', 'top')
axisPlaceableObjs.axisRefDescription('y', 'bottom', 'top'),
'If an array of axis IDs is provided, each `y` value will refer to the corresponding axis',
'(e.g., [\'y\', \'y2\'] for a rectangle means `y0` uses the `y` axis and `y1` uses the `y2` axis).',
].join(' ')
}),
},
ysizemode: {
valType: 'enumerated',
values: ['scaled', 'pixel'],
Expand Down
6 changes: 4 additions & 2 deletions src/components/shapes/calc_autorange.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ module.exports = function calcAutorange(gd) {
var yRefType = Axes.getRefType(shape.yref);

// paper and axis domain referenced shapes don't affect autorange
if(shape.xref !== 'paper' && xRefType !== 'domain') {
// TODO: implement autorange calculation for array ref shapes
if(xRefType !== 'array' && shape.xref !== 'paper' && xRefType !== 'domain') {
ax = Axes.getFromId(gd, shape.xref);

bounds = shapeBounds(ax, shape, constants.paramIsX);
Expand All @@ -31,7 +32,8 @@ module.exports = function calcAutorange(gd) {
}
}

if(shape.yref !== 'paper' && yRefType !== 'domain') {
// TODO: implement autorange calculation for array ref shapes
if(yRefType !== 'array' && shape.yref !== 'paper' && yRefType !== 'domain') {
ax = Axes.getFromId(gd, shape.yref);

bounds = shapeBounds(ax, shape, constants.paramIsY);
Expand Down
2 changes: 1 addition & 1 deletion src/components/shapes/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ module.exports = {
Q: {1: true, 3: true, drawn: 3},
C: {1: true, 3: true, 5: true, drawn: 5},
T: {1: true, drawn: 1},
S: {1: true, 3: true, drawn: 5},
S: {1: true, 3: true, drawn: 4},
// A: {1: true, 6: true},
Z: {}
},
Expand Down
174 changes: 119 additions & 55 deletions src/components/shapes/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,78 +65,142 @@ function handleShapeDefaults(shapeIn, shapeOut, fullLayout) {
var ySizeMode = coerce('ysizemode');

// positioning
var axLetters = ['x', 'y'];
for(var i = 0; i < 2; i++) {
var axLetter = axLetters[i];
['x', 'y'].forEach(axLetter => {
var attrAnchor = axLetter + 'anchor';
var sizeMode = axLetter === 'x' ? xSizeMode : ySizeMode;
var gdMock = {_fullLayout: fullLayout};
var ax;
var pos2r;
var r2pos;

// xref, yref
var axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, undefined,
'paper');
var axRefType = Axes.getRefType(axRef);

if(axRefType === 'range') {
ax = Axes.getFromId(gdMock, axRef);
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');
}
// xref, yref - handle both string and array values
var axRef;
var refAttr = axLetter + 'ref';
var inputRef = shapeIn[refAttr];

if(Array.isArray(inputRef) && inputRef.length > 0) {
// Array case: use coerceRefArray for validation
var expectedLen = helpers.countDefiningCoords(shapeType, path);
axRef = Axes.coerceRefArray(shapeIn, shapeOut, gdMock, axLetter, undefined, 'paper', expectedLen);
shapeOut['_' + axLetter + 'refArray'] = true;

// Need to register the shape with all referenced axes for redrawing purposes
axRef.forEach(function(ref) {
if(Axes.getRefType(ref) === 'range') {
ax = Axes.getFromId(gdMock, ref);
if(ax && ax._shapeIndices.indexOf(shapeOut._index) === -1) {
ax._shapeIndices.push(shapeOut._index);
}
}
});
} else {
pos2r = r2pos = Lib.identity;
// String/undefined case: use coerceRef
axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, undefined, 'paper');
}

// Coerce x0, x1, y0, y1
if(noPath) {
var dflt0 = 0.25;
var dflt1 = 0.75;

// hack until V3.0 when log has regular range behavior - make it look like other
// ranges to send to coerce, then put it back after
// this is all to give reasonable default position behavior on log axes, which is
// a pretty unimportant edge case so we could just ignore this.
var attr0 = axLetter + '0';
var attr1 = axLetter + '1';
var in0 = shapeIn[attr0];
var in1 = shapeIn[attr1];
shapeIn[attr0] = pos2r(shapeIn[attr0], true);
shapeIn[attr1] = pos2r(shapeIn[attr1], true);

if(sizeMode === 'pixel') {
coerce(attr0, 0);
coerce(attr1, 10);
if(Array.isArray(axRef)) {
var dflts = [0.25, 0.75];
var pixelDflts = [0, 10];

// For each coordinate, coerce the position with their respective axis ref
[0, 1].forEach(function(i) {
var ref = axRef[i];
var refType = Axes.getRefType(ref);
if(refType === 'range') {
ax = Axes.getFromId(gdMock, ref);
pos2r = helpers.shapePositionToRange(ax);
r2pos = helpers.rangeToShapePosition(ax);
if(ax.type === 'category' || ax.type === 'multicategory') {
coerce(axLetter + i + 'shift');
}
} else {
pos2r = r2pos = Lib.identity;
}

if(noPath) {
var attr = axLetter + i;
var inValue = shapeIn[attr];
shapeIn[attr] = pos2r(shapeIn[attr], true);

if(sizeMode === 'pixel') {
coerce(attr, pixelDflts[i]);
} else {
Axes.coercePosition(shapeOut, gdMock, coerce, ref, attr, dflts[i]);
}

shapeOut[attr] = r2pos(shapeOut[attr]);
shapeIn[attr] = inValue;
}

if(i === 0 && sizeMode === 'pixel') {
var inAnchor = shapeIn[attrAnchor];
shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);
Axes.coercePosition(shapeOut, gdMock, coerce, ref, attrAnchor, 0.25);
shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
shapeIn[attrAnchor] = inAnchor;
}
});
} else {
var axRefType = Axes.getRefType(axRef);

if(axRefType === 'range') {
ax = Axes.getFromId(gdMock, axRef);
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 {
Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr0, dflt0);
Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr1, dflt1);
pos2r = r2pos = Lib.identity;
}

// hack part 2
shapeOut[attr0] = r2pos(shapeOut[attr0]);
shapeOut[attr1] = r2pos(shapeOut[attr1]);
shapeIn[attr0] = in0;
shapeIn[attr1] = in1;
}
// Coerce x0, x1, y0, y1
if(noPath) {
var dflt0 = 0.25;
var dflt1 = 0.75;

// hack until V3.0 when log has regular range behavior - make it look like other
// ranges to send to coerce, then put it back after
// this is all to give reasonable default position behavior on log axes, which is
// a pretty unimportant edge case so we could just ignore this.
var attr0 = axLetter + '0';
var attr1 = axLetter + '1';
var in0 = shapeIn[attr0];
var in1 = shapeIn[attr1];
shapeIn[attr0] = pos2r(shapeIn[attr0], true);
shapeIn[attr1] = pos2r(shapeIn[attr1], true);

if(sizeMode === 'pixel') {
coerce(attr0, 0);
coerce(attr1, 10);
} else {
Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr0, dflt0);
Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr1, dflt1);
}

// hack part 2
shapeOut[attr0] = r2pos(shapeOut[attr0]);
shapeOut[attr1] = r2pos(shapeOut[attr1]);
shapeIn[attr0] = in0;
shapeIn[attr1] = in1;
}

// Coerce xanchor and yanchor
if(sizeMode === 'pixel') {
// Hack for log axis described above
var inAnchor = shapeIn[attrAnchor];
shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);
// Coerce xanchor and yanchor
if(sizeMode === 'pixel') {
// Hack for log axis described above
var inAnchor = shapeIn[attrAnchor];
shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);

Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attrAnchor, 0.25);
Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attrAnchor, 0.25);

// Hack part 2
shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
shapeIn[attrAnchor] = inAnchor;
// Hack part 2
shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
shapeIn[attrAnchor] = inAnchor;
}
}
}
});

if(noPath) {
Lib.noneOrAll(shapeIn, shapeOut, ['x0', 'x1', 'y0', 'y1']);
Expand Down
Loading