plotly.js icon indicating copy to clipboard operation
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

Open archmoj opened this issue 3 years ago • 11 comments
trafficstars

@plotly/plotly_js cc: @chriddyp @LiamConnors

archmoj avatar Aug 12 '22 01:08 archmoj

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:

image

nicolaskruchten avatar Aug 12 '22 12:08 nicolaskruchten

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.

archmoj avatar Aug 12 '22 14:08 archmoj

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.

nicolaskruchten avatar Aug 12 '22 17:08 nicolaskruchten

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.

alexcjohnson avatar Aug 12 '22 19:08 alexcjohnson

@LiamConnors FYI - I pushed some commits after the discussion we had.

archmoj avatar Aug 16 '22 23:08 archmoj

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:

image

@nicolaskruchten Good call. We decided to make this option available too. See https://github.com/plotly/plotly.js/pull/6297/commits/f5e269dcf8a1a5ac7208e520cfef2d455f156409.

archmoj avatar Aug 25 '22 22:08 archmoj

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: Screen Shot 2022-08-26 at 11 33 30 and gl2d: Screen Shot 2022-08-26 at 11 33 46

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.

alexcjohnson avatar Aug 26 '22 15:08 alexcjohnson

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: Screen Shot 2022-08-26 at 13 25 11

or z-marker-standoff: Screen Shot 2022-08-26 at 15 06 05

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.

alexcjohnson avatar Aug 26 '22 19:08 alexcjohnson

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: Screen Shot 2022-08-26 at 11 33 30 and gl2d: Screen Shot 2022-08-26 at 11 33 46

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.

archmoj avatar Aug 31 '22 17:08 archmoj

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: Screen Shot 2022-08-26 at 13 25 11

or z-marker-standoff: Screen Shot 2022-08-26 at 15 06 05

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.

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.

archmoj avatar Aug 31 '22 17:08 archmoj

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: Screen Shot 2022-08-26 at 11 33 30 and gl2d: Screen Shot 2022-08-26 at 11 33 46

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.

archmoj avatar Sep 19 '22 13:09 archmoj

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: Screen Shot 2022-09-26 at 08 43 35 If I explicitly set line.backoff=0 we get the right behavior: Screen Shot 2022-09-26 at 08 45 30

alexcjohnson avatar Sep 26 '22 12:09 alexcjohnson

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: Screen Shot 2022-09-26 at 08 43 35 If I explicitly set line.backoff=0 we get the right behavior: Screen Shot 2022-09-26 at 08 45 30

Good catch! Addressed in b4ef2ab.

archmoj avatar Oct 03 '22 15:10 archmoj