Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: rounded corners for trace bars #2196

Closed
Andrucis opened this issue Dec 7, 2017 · 43 comments · Fixed by #6761
Closed

Feature request: rounded corners for trace bars #2196

Andrucis opened this issue Dec 7, 2017 · 43 comments · Fixed by #6761
Labels
feature something new

Comments

@Andrucis
Copy link

Andrucis commented Dec 7, 2017

This is a request to pass attributes to trace bar component to adjust bar corner roundness.
That way bar visual variety would be extended.
Each bar corners roundness could individually adjusted with percentage value from 0 to 1 where 1 is full circle arc.

Attribute could be passed like this:
"cornerroundness": { "bottomleft": 0.3, "bottomright": 0, "topleft": 0.3, "topright": 0 }

Maximum arc radius could be set to least wide bar edge width divided by two otherwise there could arise visual problems with bar.
Visually that would result that bars would be able to look like this:
image

@alexcjohnson
Copy link
Collaborator

The dataviz purist in me is a little squeamish - the area of the bar, the most important component of its visual weight, loses proportionality with the data value, and lacking a straight line at the end it's hard to compare two similar bars and tell which is bigger. But you're right that it's a pleasant effect, we'd entertain a PR about this.

@etpinard
Copy link
Contributor

etpinard commented Dec 7, 2017

What happens when bars are stacked?

@Andrucis
Copy link
Author

Andrucis commented Dec 7, 2017

@etpinard the stacking part is a problem. Basically, one way is to manually handle it when passing data object by ensuring appropriate round corners are only on data bars that are on top or on the bottom of the stack. Of course, this isn't ideal since bar can be in different positions in different stacks. Which limits the use of round corners on stacked bars.
Another solution would be automatically omit roundness of the bar depending on its position in bar stack. But as I understand it's not quite possible at the moment to know whether there will be a bar on top or on the bottom at the moment of drawing individual bar. Also, it would require to pass bar roundness attribute to all potential data bars that could end up on stack top or the bottom.

@Andrucis
Copy link
Author

Andrucis commented Dec 18, 2017

I checked out how currently bar stacking is working and based on that added stack position calculation logic which, then is used to check whether corners need to be rounded. Which results in something like seen in the picture below.
image
Also experimenting with corner roundness I found out that it's is best to use the smallest edge of all the bars, to calculate max corner radius so all the bars look similar and there aren't any cases where taller bars look more round than very small bars. Now bars would look like this instead of the one like you can see in the top comment
image

@brivvirs
Copy link

What is the status of this feature request?

@etpinard
Copy link
Contributor

status: discussion needed.

@jhodges10
Copy link

Any updates on this?

@brivvirs
Copy link

Any updates on this?

Unfortunately no

@jamesmfriedman
Copy link

+1. I need this as well. Much more subtle but there is a need to fit charts into a brand.

@jyotishmandeori
Copy link

Any updates?

@Jonathan-MW
Copy link

Jonathan-MW commented Jun 3, 2019

I would say rounded corners are essential for professional graphs. As Steve Jobs would have said: "Even something as basic as a traffic sign has rounded corners".

For now I'll use shapes with plotly.py:
https://plot.ly/~empet/14945/shapes-that-are-filled-rectangle/#/

right-exit-traffic-sign-k-1797

@alexcjohnson
Copy link
Collaborator

FWIW the shape solution doesn't need two overlaid shapes - just take out the extra M steps between the Q and L portions. See for example https://codepen.io/alexcjohnson/pen/dErOaK?editors=0010

@empet
Copy link

empet commented Jun 4, 2019

@alexcjohnson Thanks for this simple solution to round the corners.

@Jonathan-MW I updated this notebook https://plot.ly/~empet/14945 defining the path like in the above pen.

@Romu-C
Copy link

Romu-C commented Sep 24, 2019

Any updates? This an important feature and not optional if you want to fit your chart to a design system brand.

@BrianRuizy
Copy link

Any update, 7mo later?
Though it is a very niche demand, I would love to see this feature come alive. Just noticed, even the latest Plotly logo uses rounded bars.

@mdriesch
Copy link

I'd also appreciate to see this feature.

@prykon
Copy link

prykon commented Jul 15, 2020

2.5 years later and still no round corners...please add this feature! :,(

@nicolaskruchten
Copy link
Contributor

We'd be happy to work with someone who wants to implement this and get it merged in but it's not on our roadmap at the moment :)

@BrianRuizy
Copy link

@nicolaskruchten, what would be the ideal approach to implementing this change?

@nicolaskruchten
Copy link
Contributor

Hi @BrianRuizy !

The first step would be to propose one or more new attributes in the Plotly.js schema to control this new behaviour. Something like "a new attribute cornerradius under bar.marker which accepts integers and defaults to 0" or similar. Usually this results in a bit of discussion around a spec, like "what about stacks? what about groups? what about histogram traces or waterfall traces?" etc.

Once we can agree to a 'spec' like this, it's usually a question of figuring out a test plan and then writing the code. The test plan will involve some static image tests that prove the new attribute works and some Jasmine tests to prove that it can work when turned on and off via react() and restyle and that it behaves correctly under various corner-case situations (i.e. bars that go negative etc).

@jackparmer
Copy link
Contributor

jackparmer commented Sep 10, 2020

This issue has been tagged with NEEDS SPON$OR

A community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort.

What Sponsorship includes:

  • Completion of this feature to the Sponsor's satisfaction, in a manner coherent with the rest of the Plotly.js library and API
  • Tests for this feature
  • Long-term support (continued support of this feature in the latest version of Plotly.js)
  • Documentation at plotly.com/javascript
  • Possibility of integrating this feature with Plotly Graphing Libraries (Python, R, F#, Julia, MATLAB, etc)
  • Possibility of integrating this feature with Dash
  • Feature announcement on community.plotly.com with shout out to Sponsor (or can remain anonymous)
  • Gratification of advancing the world's most downloaded, interactive scientific graphing libraries (>50M downloads across supported languages)

Please include the link to this issue when contacting us to discuss.

@tanmay-kulkarni
Copy link

tanmay-kulkarni commented Mar 5, 2021

I'm using Plotly along with Dash at my company. While Plotly is great, it pales in comparison to the overall visual pleasantness provided by Tableau. My dashboard is really heavy on Bar Charts and this feature would be most welcome. I don't know if this is very hard to implement or just not a priority. But if the kind developers out there entertain this feature request, I'd be very grateful. Judging from this thread, clearly so many people are interested in it.

@Kully
Copy link
Contributor

Kully commented May 24, 2021

One workaround is to programmatically insert a half circle on the top of your bar charts. While we are in the plotly.js repo, this is a demo with Python.

This approach relies on knowing the width of each of the bars, which you can set. If you know the widths, you can programmatically run through all the data points (x,y) in the figure, create an SVG path that starts at (x-1/2*bar_width, y), and then draw a cubic Bézier curve over to the right corner of the bar, at (x+1/2*bar_width, y).

The chart on the left is without the rounded corners, the chart of the right is with the rounded corners.

image

This is what app.py looks like:

import random

import dash
import plotly
import plotly.graph_objs as go
import dash_core_components as dcc
import dash_html_components as html


X_ARRAY = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Y_ARRAY = [10, 4, 7, 11, 12, 8, 3, 6, 6]

GRAPH_STYLE = {
    "width": "50%",
    "display": "inline-block",
}

app = dash.Dash(
    __name__,
    suppress_callback_exceptions=True,
    meta_tags=[
        {"name": "viewport", "content": "width=device-width, initial-scale=1.0"}
    ],
)
server = app.server


def bar_graph(x_array, y_array, marker_color=None):
    if not marker_color:
        marker_color = "DodgerBlue"

    fig = go.Figure(
        data=go.Bar(x=x_array, y=y_array, marker=dict(color=marker_color)),
        layout=go.Layout(
            height=300, margin=dict(l=0, r=0, t=0, b=0,), yaxis=dict(range=[0, 15])
        ),
    )

    return fig


def rounded_bar_graph(x_array, y_array, marker_color=None):
    if not marker_color:
        marker_color = "DodgerBlue"

    fig = bar_graph(x_array, y_array)

    bw = 0.4  # half of the bar width
    curve_height = 2.7
    no_color = "rgba(0,0,0,0)"
    shapes = []
    for x, y in zip(fig["data"][0]["x"], fig["data"][0]["y"]):
        path = f"""
            M {x-bw},{y}
            C {x-bw} {y+curve_height}, {x+bw} {y+curve_height}, {x+bw} {y}
            V 0
            H {x-bw}
            Z
        """
        shapes.append(
            dict(type="path", path=path, line_color=no_color, fillcolor=marker_color,)
        )
    fig.update_layout(shapes=shapes,)
    return fig


layout = html.Div(
    children=[
        dcc.Graph(
            id="bar-graph-2", style=GRAPH_STYLE, figure=bar_graph(X_ARRAY, Y_ARRAY)
        ),
        dcc.Graph(
            id="bar-graph",
            style=GRAPH_STYLE,
            figure=rounded_bar_graph(X_ARRAY, Y_ARRAY),
        ),
    ]
)
app.layout = layout

while __name__ == "__main__":
    app.run_server(debug=True)

CC
@tanmay-kulkarni @prykon @mdriesch

@nicolaskruchten
Copy link
Contributor

nicolaskruchten commented May 24, 2021

One thing to note about rounded corners is that they're (potentially, I haven't dug up any studies!) problematic for visualization in two ways:

  1. The height of the bar becomes ambiguous: is it the top of the rounded bit? The part where the rounding starts? (As in @Kully's solution... possibly misleading!) The average of the two?
  2. Beyond length-encoding problems it breaks the area-encoding of equal-width bars: short bars lose proportionally more area (if the corners are "trimmed off", or gain it if it's an added shape) than long bars, making comparisons potentially more error-prone.

@Kully
Copy link
Contributor

Kully commented May 25, 2021

  • The height of the bar becomes ambiguous: is it the top of the rounded bit? The part where the rounding starts? (As in @Kully's solution... possibly misleading!) The average of the two?

For a full half circle, I can see how this is a problem for sure. However, with only a slight border-radius to the tops of the bars, I don't think the height becomes as ambiguous.

  • Beyond length-encoding problems it breaks the area-encoding of equal-width bars: short bars lose proportionally more area (if the corners are "trimmed off", or gain it if it's an added shape) than long bars, making comparisons potentially more error-prone.

This is a really good point as well. If the point of the visualization is to compare areas, having rounded corners could absolutely mislead.

@nicolaskruchten
Copy link
Contributor

If the point of the visualization is to compare areas

One challenge with visualization is that we have little control over how people actually perceive what we produce, so even if comparing areas isn't "the point" of the visualization author, people who read it most likely will take areas into account when comparing at a glance. But yes, this is minimized when using small corner-radii compared to the bar widths.

@Kully
Copy link
Contributor

Kully commented May 25, 2021

One challenge with visualization is that we have little control over how people actually perceive what we produce

This is very true. And it tessellates so neatly with questions such as:

  • What is the most ethical/morale way or representing the data? and
  • How will a specific person actually perceive this?

@nicolaskruchten Are there any resources/books you know of that talk about these kinds of viz choices and their affect (conscious or not) on the end user/viewer?

@nicolaskruchten
Copy link
Contributor

Yes, there is lots of research about perception. A good starting point, if dated, is Colin Ware's Information Visualization: Perception for Design.

@alexcjohnson
Copy link
Collaborator

Side note: these issues also pertain in a long-dormant problem with dash-daq thermometers plotly/dash-daq#68

@Kully
Copy link
Contributor

Kully commented May 27, 2021

Side note: these issues also pertain in a long-dormant problem with dash-daq thermometers plotly/dash-daq#68

Ahh I didn't know that, thank you for the heads up.

@Kully
Copy link
Contributor

Kully commented May 27, 2021

A good starting point, if dated, is Colin Ware's Information Visualization: Perception for Design.

Thank you very much for this recommendation. I'll check it out. 🙂

CC @nicolaskruchten

@arslanhashmi
Copy link

almost 4 years now and still no round corners......

@nirnejak
Copy link

nirnejak commented Jun 2, 2022

+1 for Rounded Corners

@abdul98rehman
Copy link

just so its known, some of us are still hoping for rounded corners 5 years on

@alexcjohnson
Copy link
Collaborator

Absent a sponsor this is unlikely to make it onto Plotly's roadmap - but we'd gladly accept a PR and help get it finished, if any of the folks giving this issue a 👍 would like to give it a go!

@archmoj
Copy link
Contributor

archmoj commented Sep 29, 2022

Absent a sponsor this is unlikely to make it onto Plotly's roadmap - but we'd gladly accept a PR and help get it finished, if any of the folks giving this issue a +1 would like to give it a go!

Also for treemap & icicle traces one may be interested to expose this similar hidden feature:

var FILLET = 0; // TODO: may expose this constant

@alexcjohnson it would be great if we declare the potential (per trace?) attribute name (& a max bound e.g. 10px) for this feature to start with.

@mebaysan
Copy link

Hi all. I've handled this feature by using scatter charts! You can see the code in my Gist

2

1

@axel-rock
Copy link

I found a quick workaround in CSS if anyone is interested:

Find the CSS selector for the bar(s) you want to make to have rounded. It can be done by selecting it in your inspector panel, and click "Copy > Copy Selector".

Then, apply a CSS inset clip path.

In my case, I just wanted some bars to be rounded, here is how it looked like:

svg:nth-child(1) > g.cartesianlayer > g > g.plot > g > g:nth-child(2) > g > g:nth-child(1) > path,
svg:nth-child(1) > g.cartesianlayer > g > g.plot > g > g:nth-child(2) > g > g:nth-child(2) > path  {
  clip-path: inset(0% 0% 0% 0% round 100px);
}

image

I hope it helps someone

@QuentinLuc
Copy link

The solution provided by @axel-rock works well for a regular bar chart. In the case of a stacked bar chart, I didn't find a way to target only the start plot and trailing plot for the rounded cornes. The SVGs are positionned "randomly" and there is no group and similar className between plot making a bar.

@rika8aga
Copy link

+1

@andradelis
Copy link

+1

@loxux
Copy link

loxux commented Jan 8, 2024

The solution provided by @axel-rock works well for a regular bar chart. In the case of a stacked bar chart, I didn't find a way to target only the start plot and trailing plot for the rounded cornes. The SVGs are positionned "randomly" and there is no group and similar className between plot making a bar.

I think for stacked bar chart should work the follow:

#stacked-bar-chart > div.js-plotly-plot > div > div > svg > g.cartesianlayer > g > g.plot > g > g:nth-last-child(1) > g > g > path {
    clip-path: inset(0% 0% 0% 0% round 0.3rem 0.3rem 0 0);
}

Where #stacked-bar-chart - the id of the graph container. g:nth-last-child(1) - this part is for choosing only top bars, round 0.3rem 0.3rem 0 0 - this is for round only top of the bar

And a bit update on @axel-rock great solution, to apply rounded corner for the entire chart, you can apply following style:

#id-of-the-bar-chart > div.js-plotly-plot > div > div > svg > g.cartesianlayer > g > g.plot > g > g > g > g > path {
  clip-path: inset(0% 0% 0% 0% round 0.3rem);
}

This works fine with vertical/horizontal bars and histograms, you just need to change #id-of-the-bar-chart to the actual graph id

@sanjaykhanssk
Copy link

I found a quick workaround in CSS if anyone is interested:

Find the CSS selector for the bar(s) you want to make to have rounded. It can be done by selecting it in your inspector panel, and click "Copy > Copy Selector".

Then, apply a CSS inset clip path.

In my case, I just wanted some bars to be rounded, here is how it looked like:

svg:nth-child(1) > g.cartesianlayer > g > g.plot > g > g:nth-child(2) > g > g:nth-child(1) > path,
svg:nth-child(1) > g.cartesianlayer > g > g.plot > g > g:nth-child(2) > g > g:nth-child(2) > path  {
  clip-path: inset(0% 0% 0% 0% round 100px);
}
image

I hope it helps someone

For me it only modified the second bar in a grouped bar so i modified it to

svg:nth-child(1) > g.cartesianlayer > g > g.plot > g > g > g > g > path, svg:nth-child(1) > g.cartesianlayer > g > g.plot > g > g > g > g > path { clip-path: inset(0% 0% 0% 0% round 8px); }

With modification:

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature something new
Projects
None yet
Development

Successfully merging a pull request may close this issue.