Skip to content

fix: Handle 'pixel' size mode for shape labels#7790

Open
camdecoster wants to merge 7 commits intomasterfrom
cam/7749/shape-text-position-pixel-size-mode
Open

fix: Handle 'pixel' size mode for shape labels#7790
camdecoster wants to merge 7 commits intomasterfrom
cam/7749/shape-text-position-pixel-size-mode

Conversation

@camdecoster
Copy link
Copy Markdown
Contributor

@camdecoster camdecoster commented May 1, 2026

Description

Update shape label positioning logic to handle pixel size mode.

Closes #7749.

Changes

  • Update position calculations
  • Export helper to aid in calculations
  • Add tests
  • Add mock with baseline image

Screenshots

Before After
image image

Testing

  • Be on master
  • Run devtools
  • Add new mock temporarily on branch
  • Load the mock
  • Note that the labels are missing
  • Switch to this branch
  • Load the mock again
  • Notes that the labels are positioned correctly

@camdecoster camdecoster self-assigned this May 1, 2026
@camdecoster
Copy link
Copy Markdown
Contributor Author

The initial mock validation failed because one of the shape types was listed as "rectangle", which isn't valid. "rect" is what should have been listed. I didn't notice this because the default shape used when an invalid type is passed in is... "rect". Thanks CI, for keeping us honest.

@camdecoster camdecoster marked this pull request as ready for review May 1, 2026 22:16
@camdecoster camdecoster assigned emilykl and unassigned camdecoster May 5, 2026
Comment on lines +107 to +130
const x2p = (v, shift, xa, xRefType) => helpers.getDataToPixel(gd, xa, shift, false, xRefType)(v);
const y2p = (v, shift, ya, yRefType) => helpers.getDataToPixel(gd, ya, shift, true, yRefType)(v);
// When using pixel offset mode it's necessary to add the anchor position for the
// correct final value
if (options.xsizemode === 'pixel') {
const xAnchorPos = x2p(options.xanchor, undefined, xa0, xRefType0);
const xShift0 = helpers.getPixelShift(xa0, options.x0shift);
const xShift1 = helpers.getPixelShift(xa0, options.x1shift);
shapex0 = xAnchorPos + options.x0 + xShift0;
shapex1 = xAnchorPos + options.x1 + xShift1;
} else {
shapex0 = x2p(options.x0, options.x0shift, xa0, xRefType0);
shapex1 = x2p(options.x1, options.x1shift, xa1, xRefType1);
}
if (options.ysizemode === 'pixel') {
const yAnchorPos = y2p(options.yanchor, undefined, ya0, yRefType0);
const yShift0 = helpers.getPixelShift(ya0, options.y0shift);
const yShift1 = helpers.getPixelShift(ya0, options.y1shift);
shapey0 = yAnchorPos - options.y0 + yShift0;
shapey1 = yAnchorPos - options.y1 + yShift1;
} else {
shapey0 = y2p(options.y0, options.y0shift, ya0, yRefType0);
shapey1 = y2p(options.y1, options.y1shift, ya1, yRefType1);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If I'm understanding the issue correctly (when xsizemode is "pixel", we are supposed to determine shapex0 and shapex1 by first converting xanchor from data to pixels and THEN adding x0 and x1 which are already given in pixels)

... would something like the following work? It feels more intuitive to me, but could be just me.

Suggested change
const x2p = (v, shift, xa, xRefType) => helpers.getDataToPixel(gd, xa, shift, false, xRefType)(v);
const y2p = (v, shift, ya, yRefType) => helpers.getDataToPixel(gd, ya, shift, true, yRefType)(v);
// When using pixel offset mode it's necessary to add the anchor position for the
// correct final value
if (options.xsizemode === 'pixel') {
const xAnchorPos = x2p(options.xanchor, undefined, xa0, xRefType0);
const xShift0 = helpers.getPixelShift(xa0, options.x0shift);
const xShift1 = helpers.getPixelShift(xa0, options.x1shift);
shapex0 = xAnchorPos + options.x0 + xShift0;
shapex1 = xAnchorPos + options.x1 + xShift1;
} else {
shapex0 = x2p(options.x0, options.x0shift, xa0, xRefType0);
shapex1 = x2p(options.x1, options.x1shift, xa1, xRefType1);
}
if (options.ysizemode === 'pixel') {
const yAnchorPos = y2p(options.yanchor, undefined, ya0, yRefType0);
const yShift0 = helpers.getPixelShift(ya0, options.y0shift);
const yShift1 = helpers.getPixelShift(ya0, options.y1shift);
shapey0 = yAnchorPos - options.y0 + yShift0;
shapey1 = yAnchorPos - options.y1 + yShift1;
} else {
shapey0 = y2p(options.y0, options.y0shift, ya0, yRefType0);
shapey1 = y2p(options.y1, options.y1shift, ya1, yRefType1);
}
var x2p, y2p;
// When using pixel offset mode it's necessary to compute position relative to xanchor/yanchor
if (options.xsizemode === 'pixel') {
x2p = (v, shift, xa, xRefType) => helpers.getDataToPixel(gd, xa, shift, false, xRefType)(options.xanchor) + v;
} else {
x2p = (v, shift, xa, xRefType) => helpers.getDataToPixel(gd, xa, shift, false, xRefType)(v);
}
if (options.ysizemode === 'pixel') {
y2p = (v, shift, ya, yRefType) => helpers.getDataToPixel(gd, ya, shift, true, yRefType)(options.yanchor) + v;
} else {
y2p = (v, shift, ya, yRefType) => helpers.getDataToPixel(gd, ya, shift, true, yRefType)(v);
}
shapex0 = x2p(options.x0, options.x0shift, xa0, xRefType0);
shapex1 = x2p(options.x1, options.x1shift, xa1, xRefType1);
shapey0 = y2p(options.y0, options.y0shift, ya0, yRefType0);
shapey1 = y2p(options.y1, options.y1shift, ya1, yRefType1);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Your understanding is correct. I actually think the current code is clearer for me, so I'd like to stick with that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@camdecoster Would it be possible to edit the existing shape label mock rather than adding a new one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Which mock are you referring to?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Any of the ones starting with text_on_shapes_.

text_on_shapes_basic is probably the easiest to add to.

emilykl
emilykl previously approved these changes May 7, 2026
Copy link
Copy Markdown
Contributor

@emilykl emilykl left a comment

Choose a reason for hiding this comment

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

@camdecoster Looks good as-is, left a couple comments with suggestions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: Text in circle and rectangle shapes is positioned incorrectly

2 participants