Skip to content

Fixed size circle shape #2193

@chriddyp

Description

@chriddyp
Member

I'm helping out a Dash community user who could use interactive shape circles (https://community.plot.ly/t/moving-the-location-of-a-graph-point-interactively/7161/1)

I'd like a shape circle that has a fixed size in points but is positioned relative to data - something like a marker point that I can drag around but not necessarily resize.

Right now, circle shapes change size when you drag them around:
circle shape

This is somewhat related to #2038

Activity

added this to the Dash milestone on Dec 5, 2017
alexcjohnson

alexcjohnson commented on Dec 5, 2017

@alexcjohnson
Collaborator

Cool idea, and there are a lot of variants of this that could be useful. Even for (fully or partially) paper-referenced shapes, it might be nice to be able to size them in px, like if you want to draw your own arrow pointing to a particular axis value, or you have a logo or something that you want a fixed size even if the plot resizes.

Seems to me the most flexible way to do this would be adding attributes like:

xmode: 'scaled' | 'pixel' // scaled (dflt) -> x0/x1 are data vals or plot units, anchorx is ignored
                          // pixel -> x0/x1 are pixels relative to anchorx
anchorx: (any) // data val or plot fraction, depending on `xref`
ymode, anchory: same for y

Not sure about the naming, but that would leave the flexibility to do things like make a shape off to the side of a data point, like (x|y)shift for annotations, not just centered.

rmoestl

rmoestl commented on Mar 19, 2018

@rmoestl
Contributor

I think it is worth noting that the API suggested by @alexcjohnson will support two different uses cases:

  1. Scenario A: to render a fixed size shape positioned relative to data (blue rectangle below) as suggested by @chriddyp. E.g. useful to annotate a specific data point.
  2. Scenario B: to render fixed size shapes relative to the plotting area (red rectangle). E.g. useful to place an icon of some sort at the top right corner of a plot.

Despite it increases the API surface, I can clearly see the benefit of having an anchor. When annotating a specific data point you certainly would want the control to render it 3px left the data point, for example.

Regarding the naming, I find anchorx quite good. Might xsizing be more clear than xmode? But I also saw "mode" as parts of an attribute name already. So xmode might be more consistent within plotly.js...

One question, though: Should we take the dimensions of a fixed size shape into account when calculating the range of an axis?
It is clear to factor in the position of a fixed size shape, but when it comes to the dimensions, it could become hairy, right? It could easily be the case that a fixed size shape overflows the plotting area (the green rectangle). To prevent this, we would need to know how "much data" is worth one pixel at the time of calculating the axis range.

2193_fixed-size-shape-scenarios

etpinard

etpinard commented on Mar 19, 2018

@etpinard
Contributor

This reminds me of how we specify layout images. Attributes sizex, sizey, xanchor, yanchor and sizing could be used in scatter markers too.

rmoestl

rmoestl commented on Mar 21, 2018

@rmoestl
Contributor

Thanks @etpinard for pointing me into that direction. Here are my thoughts.

API

images layout attributes xanchor and yanchor are about an image's alignment – a different meaning than what we have in the initial proposal for fixed size shapes. Also, image sizex and sizey can't have pixel values.

So because of that, I stuck with the semantics of @alexcjohnson's initial proposal for the time being. Although with slightly different naming. Here's what the API currently looks like in my local branch.

// Shape positioned relative to data
shapes: [{
    type: 'rect',
    xref: 'x',
    yref: 'y',
    xsizemode: 'pixel',
    ysizemode: 'pixel',
    xanchor: '3',
    yanchor: '0',
    x0: 3,
    x1: 25,
    y0: 0,
    y1: 25
}]
// Shape positioned relative to plot area, in this case top right corner
shapes: [{
    type: 'rect',
    xref: 'paper',
    yref: 'paper',
    xsizemode: 'pixel',
    ysizemode: 'pixel',
    xanchor: '1',
    yanchor: '1',
    x0: 3,
    x1: 25,
    y0: 0,
    y1: 25
}]

Any feedback on that?
By the way, one can size by pixel horizontally and relative to data vertically and vice versa.

Autorange

When calculating an axis range the xanchor and yanchor coordinates are taken into account. However, it still needs to be decided whether to factor in the size and position of the shape as well. I lean towards not factoring that in. What do you think?

etpinard

etpinard commented on Mar 21, 2018

@etpinard
Contributor

Any feedback on that?

👍 from me. Nice!

When calculating an axis range the xanchor and yanchor coordinates are taken into account. However, it still needs to be decided whether to factor in the size and position of the shape as well. I lean towards not factoring that in. What do you think?

In calc_autorange.js, we already take into account the shapes' bounds on auto-ranged graphs. It shouldn't be too hard to extend that to pixel size modes using the ax.p2c (for pixel-to-calculated data) convert method.

alexcjohnson

alexcjohnson commented on Mar 21, 2018

@alexcjohnson
Collaborator

Yes, please do include the whole shape in autorange. But don't use ax.p2c to do it - that assumes the axis scaling (ie range & length) is already known, and a) that's what we're trying to calculate with Axes.expand, and b) we'd like this calculation to not need to be redone when the plot changes size, margins, etc.

Fortunately we DO have this built into Axes.expand, in the form of ppad (pixel padding) and its asymmetric variants ppadplus and ppadminus -
https://github.com/plotly/plotly.js/blob/master/src/plots/cartesian/autorange.js#L228-L230

rmoestl

rmoestl commented on Mar 28, 2018

@rmoestl
Contributor

Thanks @alexcjohnson, I've now implemented the correct auto-ranging for lines, rectangles and circles.

However, I wonder what we should do about path shapes. We haven't really discussed that in this thread except @alexcjohnson mentioned arrows in his initial reply. Should we allow pixel sized path shapes as well? It would certainly be a cool feature. But I'm not sure if enough people would use it so that the effort is worth it.

alexcjohnson

alexcjohnson commented on Mar 28, 2018

@alexcjohnson
Collaborator

Yes, it'll be more effort to get path working with pixel sizing - but I think we should do it. We don't want any more exceptions in the API than we really need, and even if very few people use it, we'll be able to make some extra-cool demos with this feature.

chriddyp

chriddyp commented on Mar 28, 2018

@chriddyp
MemberAuthor

Yes, it'll be more effort to get path working with pixel sizing - but I think we should do it
we'll be able to make some extra-cool demos with this feature

Yeah, that's pretty interesting. I wonder if this will allow the user to draw an entire line plot and then drag around the vertices, which is what the Dash community member originally asked about in https://community.plot.ly/t/moving-the-location-of-a-graph-point-interactively/7161:

I want to give the user the ability to drag a point on a line graph either up or down, thereby changing the underlying y-axis data (what if analysis). This would then change other charts on the dashboard.
Is this type of interactivity possible through Dash (clicking on and moving a point)?

alexcjohnson

alexcjohnson commented on Mar 28, 2018

@alexcjohnson
Collaborator

We don't support editing vertices of path shapes by GUI currently, and while that could be interesting, I feel like dragging data points themselves would be a more valuable feature to add first. Not in this issue though, we can make another issue if there's sufficient interest.

rmoestl

rmoestl commented on Mar 28, 2018

@rmoestl
Contributor

Question about how the coordinate system for specifying fixed size shapes will be expected by the users: The positive x direction will point to the right.

But what about the positive y direction? Will it point to the bottom (as in SVG) or to the top (like the plot's coordinate system)? First, I leaned towards 'to the top'. But I suppose most users are coders, so they might be familiar with positive y-axis pointing 'to the bottom'.

alexcjohnson

alexcjohnson commented on Mar 28, 2018

@alexcjohnson
Collaborator

It's true, this is a weird point that requires a lot of minus signs in our code, but I think positive y should be toward the top, no matter whether it's data (unless you explicitly flip the y axis), plot fraction, or pixels.

There's one exception to this, that I know of anyway, and that's annotations[i].ay, which is to the bottom. I think elsewhere "to the top" is consistent enough that we should make anything new be "to the top", and in v2.0 we should flip ay to match (or even before, if we couple the fix with giving this attribute a nicer name, then we can do it in a backward-compatible way).

etpinard

etpinard commented on Mar 28, 2018

@etpinard
Contributor

but I think positive y should be toward the top, no matter whether it's data

+1

1 remaining item

alexcjohnson

alexcjohnson commented on Apr 4, 2018

@alexcjohnson
Collaborator

That's probably the only place we parse a d path attribute. See if you can generalize that code into a form you can reuse, and feel free to move it to another file that both shapes/draw and your addition (in shapes/calc_autorange?) can import from.

rmoestl

rmoestl commented on Apr 5, 2018

@rmoestl
Contributor

For a pixel-sized shape (rect, line, circle), should x1/y1 be the width/height of the shape relative to x0/y0 or should x1/y1 be relative to xanchor/yanchor? In the latter case width/height would be the distance between x0 and x1/y0 and y1.

I'm asking because right now I've implemented the former case with x1/y1 being the width/height. Unfortunately I overlooked @alexcjohnson initial spec saying

x0/x1 are pixels relative to anchorx

That describes the latter case. I see pros and cons for both approaches.

x1/y1 being the width/height

  • Pros
    • easier for user to specify a square, a circle, the length of a line
  • Cons
    • inconsistent to existing shapes API
    • a bit harder to implement, therefore less maintainable code

Pros and cons are the reverse of the above for x1/y1 being absolute (anchor is 0/0).

I should have implemented the initial spec in the first place, sorry. Anyways, there's now the chance to decide if x1/y1 being the width/height is the better way.

rmoestl

rmoestl commented on Apr 5, 2018

@rmoestl
Contributor

In today's check-in meeting we've decided the following: x1 and y1 are going to be absolute coordinates with xanchor and yanchor being the origin. There are certain situations where own way of specifying the dimension would be favorable over the other and vice versa. The absolute approach chosen is better for the most likely case: centering a shape (e.g. a circle) on a data point. And in addition to that, it is more consistent with the existing shapes API.

For the question 'When being moved, should the anchor or the x and y coordinates of a fixed size shape be changed?' it was concluded that the anchor should. This approach is resembling common UI patterns implemented in apps such as Google Slides.

etpinard

etpinard commented on Apr 11, 2018

@etpinard
Contributor

@rmoestl did #2532 close this issue?

rmoestl

rmoestl commented on Apr 12, 2018

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @chriddyp@alexcjohnson@rmoestl@etpinard

        Issue actions

          Fixed size circle shape · Issue #2193 · plotly/plotly.js