Skip to content

Conversation

@alexshoe
Copy link
Contributor

@alexshoe alexshoe commented Dec 4, 2025

Description:

Extends xref and yref to accept arrays, allowing shapes to span multiple subplots with each vertex anchored to a different axis. See #7151 for more information.

Example:

shapes: [{  
  type: 'rect',  
  x0: 0, 
  x1: 1, 
  y0: 0, 
  y1: 1,  
  xref: ['x', 'x2'],  // x0 uses 'x', x1 uses 'x2'  
  yref: ['y', 'y2']   // y0 uses 'y', y1 uses 'y2'
}]

Progress:

  • Step 1: Attributes & validation
    • Extend xref and yref to allow array values
    • Add helper function to check number of defining coordinates of a shape
    • Update value validation to work with array values
  • Step 2: Refactor coordinate conversion
  • Step 3: Update shape rendering
  • Step 4: Addressing editable shapes
  • Step 5: Tests + documentation

@alexshoe alexshoe marked this pull request as draft December 4, 2025 22:16
Comment on lines +66 to +70
// for each path command, check if there is a drawn coordinate
var segmentType = segment.charAt(0);
var hasDrawnX = constants.paramIsX[segmentType].drawn !== undefined;
var hasDrawnY = constants.paramIsY[segmentType].drawn !== undefined;
if(hasDrawnX || hasDrawnY) coordCount++;
Copy link
Contributor

@emilykl emilykl Dec 4, 2025

Choose a reason for hiding this comment

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

Hmm. H and V segments cause an odd usability issue I hadn't considered: if a path contains H and V segments then the expected lengths of xref and yref may be different, and the corresponding list items will be "out of sync" with respect to which path segments they correspond to (i.e. it will not always be the case that xref[n] and yref[n] refer to the same path segment).

For example here is a rectangular path: M0,0H10V5H0Z (see visualization)

There are four segments (not counting Z), but only 3 x-values and 2 y-values are needed to define the shape. So you would have:

{
    "path": "M0,0H10V5H0Z",
    "xref": ["x", "x2", "x"],
    "yref": ["y", "y2"],
}

which is a bit odd, and sort a high cognitive load for the user to figure out the right xref and yref lists.

That said, it seems probably better than the other alternative that comes to mind, which would be to force the user to add an xref and a yref for every single segment, even if it will be ignored for some segments.

}
}

containerOut[refAttr] = axRef;
Copy link
Contributor

Choose a reason for hiding this comment

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

The core Lib.coerce() function uses more complex logic here for setting the attribute in the out container -- I'm not 100% sure whether that's needed here, but something to verify.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

just documenting that we chatted about this and decided we'd look deeper into Lib.coerce + if it's alright to not call it here.

Comment on lines +90 to +95
if(Axes.getRefType(ref) === 'range') {
ax = Axes.getFromId(gdMock, ref);
if(ax && ax._shapeIndices.indexOf(shapeOut._index) === -1) {
ax._shapeIndices.push(shapeOut._index);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Does anything need to happen in the else case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't believe so since a paper value would not have a valid axis to register with, and paper/domain values wouldn't require a redraw in case of panning/changes to the axis.

Comment on lines +64 to +72
var coordCount = 0;
segments.forEach(function(segment) {
// for each path command, check if there is a drawn coordinate
var segmentType = segment.charAt(0);
var hasDrawnX = constants.paramIsX[segmentType].drawn !== undefined;
var hasDrawnY = constants.paramIsY[segmentType].drawn !== undefined;
if(hasDrawnX || hasDrawnY) coordCount++;
});
return coordCount;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you transform this into an Array.reduce call and return that?

* but can be prefixed, like 'ax' for annotation's arrow x
* dflt: the default to coerce to, or blank to use the first axis (falling back on
* extraOption if there is no axis)
* extraOption: aside from existing axes with this letter, what non-axis value is allowed?
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you mean for this to be a question?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, I just copied this from the original coerceRef comment, I'll fix this

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.

3 participants