plotly.js
plotly.js copied to clipboard
Add `angle`, `angleref` and `standoff` to `marker` and add `backoff` to `line` as well as adding two new arrow symbols
@plotly/plotly_js cc: @chriddyp @LiamConnors
Super exciting!
With this PR, can we have e.g. variable-sized circular markers with arrowheads pointing to them without overlapping them? This is pretty common in maps with paths, and in network diagrams:
Super exciting!
With this PR, can we have e.g. variable-sized circular markers with arrowheads pointing to them without overlapping them?
I could introduce that functionality via marker.offset or something.
That probably wouldn't be enough, as to get the effect above, we'd need circular (or square or diamond or...) markers and arrows and for the offset to be dynamic i.e. to match the marker size.
Let's leave arrowheads+markers out of this PR. If we did want to do that, I suspect it'd be better to add a separate arrowhead option than to try and shoehorn it into the marker framework - and at that point it could automatically back off to the edge of the marker.
Note for annotations, we called this "pull back from the target point" standoff. We could certainly add that on its own, which would allow certain effects people might occasionally want - use a centered marker as an arrow for example, or give the marker an outline and point the edge of the outline at the target point. And it would allow particularly motivated users to hack your arrowheads+markers too :)
But again, not for this PR.
@LiamConnors FYI - I pushed some commits after the discussion we had.
Super exciting!
With this PR, can we have e.g. variable-sized circular markers with arrowheads pointing to them without overlapping them? This is pretty common in maps with paths, and in network diagrams:
![]()
@nicolaskruchten Good call. We decided to make this option available too. See https://github.com/plotly/plotly.js/pull/6297/commits/f5e269dcf8a1a5ac7208e520cfef2d455f156409.
Looks like gl2d has a problem with the original arrow symbols... clipping them but also not drawing the bars when requested. From the test images (open variant as it's easiest to see, but applies to all variants), here's svg:
and gl2d:

The missing bars may not be new in this PR, but the clipping I'm guessing is new (or at least more prominent now) since it's based on the angle.
We'll need to back off the end of the line, when using angleref='previous' and angle=0 so it's hidden under the marker to the extent possible - obviously a wide enough line can't have its end hidden by the marker, but a medium-width line can. For annotations we did this by giving each arrowhead a specific backoff parameter that causes the line to end where its center is definitely under the arrowhead, but at the widest point possible to accommodate the largest possible width.
You can see the problem a little bit in some of the existing mocks, for example polar-direction there's some pink poking out the front of the blue markers. But it becomes more blatant if you make the line width bigger, for example on z-line-shape-arrow:

or z-marker-standoff:

This backoff should only happen at the end of a path; if the path continues through the point in question it should be unchanged. This might produce unexpected results when you add an extra marker-only trace like z-marker-standoff, because you've got two segments and only the second one will pull the line back. But I don't think it makes sense otherwise - you could argue that if there's a nonzero standoff we should pull the line back anyway, but users might add a standoff without an extra marker in order to account for marker.line.width so I don't think we can rely on that. To get the effect where all segments are pulled back you can always double up those middle points and put a null between.
Looks like
gl2dhas a problem with the original arrow symbols... clipping them but also not drawing the bars when requested. From the test images (open variant as it's easiest to see, but applies to all variants), here's svg:and gl2d:
The missing bars may not be new in this PR, but the clipping I'm guessing is new (or at least more prominent now) since it's based on the angle.
Thanks for the note. We should have spent more time designing those particular arrow heads. IMHO in a separate PR we could scale them down a bit so that they fit nicely in the Web-GL framework.
We'll need to back off the end of the line, when using
angleref='previous'andangle=0so it's hidden under the marker to the extent possible - obviously a wide enough line can't have its end hidden by the marker, but a medium-width line can. For annotations we did this by giving each arrowhead a specificbackoffparameter that causes the line to end where its center is definitely under the arrowhead, but at the widest point possible to accommodate the largest possible width.You can see the problem a little bit in some of the existing mocks, for example
polar-directionthere's some pink poking out the front of the blue markers. But it becomes more blatant if you make the line width bigger, for example onz-line-shape-arrow:or
z-marker-standoff:This backoff should only happen at the end of a path; if the path continues through the point in question it should be unchanged. This might produce unexpected results when you add an extra marker-only trace like
z-marker-standoff, because you've got two segments and only the second one will pull the line back. But I don't think it makes sense otherwise - you could argue that if there's a nonzerostandoffwe should pull the line back anyway, but users might add a standoff without an extra marker in order to account formarker.line.widthso I don't think we can rely on that. To get the effect where all segments are pulled back you can always double up those middle points and put anullbetween.
In this regards, the cleanest option I can think of would be to add another standoff (or backoff) parameter to line. In a separate PR we may add stroke-linejoin too. For now people can simply use thin lines with arrows or use other styling options (e.g. color or dash) when using thick lines.
Looks like
gl2dhas a problem with the original arrow symbols... clipping them but also not drawing the bars when requested. From the test images (open variant as it's easiest to see, but applies to all variants), here's svg:and gl2d:
The missing bars may not be new in this PR, but the clipping I'm guessing is new (or at least more prominent now) since it's based on the angle.
I'd be great to propose potential changes to address this edge case in a separate PR. I'll open an issue for it.
Looks like we're adding backoffs and prohibiting spline in some cases we shouldn't. For example:
Plotly.newPlot(gd,[{
x:[0,1,1], y:[0,1,0],
marker:{size:50, symbol:'arrow'},
line:{width:20, shape:'spline'}
}],
{width:400,height:400})
With or without asking for spline there, the implied line.backoff should have been 0 because angleref isn't included so it defaults to up, and thus spline should have been accepted. Instead we get:
If I explicitly set line.backoff=0 we get the right behavior:

Looks like we're adding backoffs and prohibiting
splinein some cases we shouldn't. For example:Plotly.newPlot(gd,[{ x:[0,1,1], y:[0,1,0], marker:{size:50, symbol:'arrow'}, line:{width:20, shape:'spline'} }], {width:400,height:400})With or without asking for
splinethere, the impliedline.backoffshould have been 0 becauseanglerefisn't included so it defaults toup, and thussplineshould have been accepted. Instead we get:If I explicitly set
line.backoff=0we get the right behavior:
Good catch! Addressed in b4ef2ab.