Skip to content

Implement bar texts (issue 34) #4

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

Open
wants to merge 37 commits into
base: master
Choose a base branch
from

Conversation

n-riesco
Copy link
Owner

@n-riesco n-riesco commented Nov 8, 2016

This PR implements attributes textposition, textfont, insidetextfont and outsidetextfont for bar traces.

I've followed this comment as close possible:

  • all these attributes accept arrays
  • default textposition is none
  • if textposition was set to auto, the inside options is preferred over outside
  • if outside was requested, but the bar is stacked under another bar, then the text is drawn inside
  • if inside was requested, but the bar is too small, then no text is drawn

I've also added jasmine tests and mocks exercising the new functionality.

* Added attributes textposition, textfont, insidetextfont and
  outsidetextfont.

* All these attributes accept arrays.
.data(Lib.identity)
.enter().append('path')
.enter().append('g').classed('point', true)

Choose a reason for hiding this comment

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

The funny indentation this had is fairly standard with d3, to mark operations within a chain that change the selection. Not sure how consistent we've been with that but it was done that way intentionally.

if(hasOutside) {
coerceArray('outsidetextfont', coerceFont, textFont, layout.font);
}
}

Choose a reason for hiding this comment

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

It looks like you've based this on pie/defaults but you've got more logic around arrays in the input. So two questions:

  • for the cases that are covered by pie, does this give the same inheritance from layout.font and textfont to insidetextfont and outsidetextfont, particularly around cases where parts of the font come from one default and other parts come from the other one? for example if layout.font supplies the family, textfont supplies the size, and insidetextfont makes it white.
  • what extra cases does this cover and should we extend pie to cover these too?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Yes, I've tried to replicate the behaviour of pie traces. This is tested here. The test is a good example of the extra cases handled here.

The main difference is that these attributes now also accept arrays (so that fonts can be defined per bar).

I think a lot of the functionality implemented in this file could be moved to Lib and reused by pie traces.

Choose a reason for hiding this comment

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

Cool. Looks like the inheritance for non-arrays is as I'd expect 👍 . For arrays though, I'm not sure about the structure - I'd think for consistency with data attributes and src tags, it would be better to have family, size, and color be arrays, rather than have font be an array of objects.

Copy link

Choose a reason for hiding this comment

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

We don't coerce entries inside data arrays or arrayOk attributes.

Non-text entries are usually via fallbacks later on in the pipeline e.g. in the drawing step for scatter text here.

The reason being: we try to make Plots.supplyDefaults scale ~ number of attribute as opposed to ~ length of the data.

Copy link

Choose a reason for hiding this comment

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

What the pie trace type is currently doing (here) looks sufficient to me at the default step.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@etpinard I don't understand the phrase "to make Plots.supplyDefaults scale ~ number of attribute"? Could you give me an example?

Copy link

@etpinard etpinard Nov 9, 2016

Choose a reason for hiding this comment

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

As in: there are about as many coerce statements as there are attributes.

What you added above has about as many coerce statements as there are items in the text attribute arrays.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@etpinard What are the reasons against making Plots.supplyDefaults scale with the data? Note that the calls to nestedProperty would still scale only with the number of attributes.

Having uncoerced data in arrays after running Plots.supplyDefaults means that we have to coerce this data in Bar.plot. This increases the number of places where untrusted user input can brake Plotly.

Another disadvantage of moving the code for array coercion from Bar.supplyDefaults into Bar.plot (instead of Lib) is that pie traces wouldn't be able to reuse the code.

Copy link

@etpinard etpinard Nov 9, 2016

Choose a reason for hiding this comment

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

Perf over DRY.

supplyDefaults gets called a lot more often than .plot.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@etpinard OK, I understand. Thx for the explanation.


if(textPosition === 'inside') {
if(barIsTooSmall) return;
}

Choose a reason for hiding this comment

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

For pies we did this a bit differently - in 'inside' mode we would always draw the text inside the slice, and shrink it if need be so it would fit (no matter how small the space is). And in 'auto' mode we would first draw it inside, then if it had to be scaled down to fit inside we'd move it outside.

This algorithm should be easier with a rectangular bar than with a wedge-shaped pie slice - do you want to give it a try?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Yes, no problem. One question though. If a text doesn't fit, shall I consider rotating the text before moving it outside?

Choose a reason for hiding this comment

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

If a text doesn't fit, shall I consider rotating the text before moving it outside?

Yes, I think so. Basically do everything you'd do with explicitly inside text, which includes seeing if you can make it bigger by rotating it, and only after that see if you can make it bigger by moving it outside. I guess even outside text can get squished here if it's wider than the bar... so it would be silly to move the text outside and still not be able to expand it!

Choose a reason for hiding this comment

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

makes me wonder if we need an attribute textorientation or something, that would be 'auto' (whatever is biggest), 'horizontal', or 'vertical'.

Copy link

Choose a reason for hiding this comment

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

I consider rotating the text before moving it outside?

I think so, as long as the text can only be horizontal or vertical (no diagonal allowed).

Copy link
Owner Author

@n-riesco n-riesco Nov 11, 2016

Choose a reason for hiding this comment

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

@alexcjohnson I've set a 3px padding. This means that unless a bar is larger than 6px, no labels could be drawn inside. Is it OK not to draw the label in those cases? Or shall I remove the padding if the bar is 6px or less?

Choose a reason for hiding this comment

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

FYI @alexcjohnson will be offline until Monday.

Choose a reason for hiding this comment

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

6px sounds ok to me.

Choose a reason for hiding this comment

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

An alternative that might be more analogous to what we did with pies (where we didn't explicitly include padding, but fitting text into a rectangle into a wedge effectively results in some padding in most cases) would be to scale down the padding by the same factor you use to scale down the label. The only context I see this making a big difference is print / pdf, where hover & zoom are unavailable but the interested viewer may be able to zoom in the document (or hold it up to their nose) and still read it.

Not blocking - we can always tweak this later if it becomes important - but I think it would be a bit nicer that way.

"x":[1,2,3,4],
"type":"bar"
}, {
"text":[0,"very small size",-3,-2],

Choose a reason for hiding this comment

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

is there a criterion by which we should rotate an outside label ('very small size') to vertical like you do with inside labels ('Three')?

Also, and this applies to all the inside labels, I'd have expected inside labels to be pushed next to the bar end, the same way as you have outside labels right on the other side of the bar end. It looks like some people want the labels in the middle, so perhaps this should be an option (perhaps textalign: middle or end) but I think semantically it's a better default to draw people's eyes toward the bar end. Seems like that's somewhat more common as well.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@alexcjohnson Without going into the details, the idea is that if a text doesn't fit inside a bar, we shrink the text by computing a scaling factor (smaller than 1).

To avoid shrinking the text unnecessarily or too much, the text is rotated 90 degrees when this results in a larger scaling factor (still smaller than 1).


I'm not sure if I did a good job with the above explanation. If not, I can give an example.

Choose a reason for hiding this comment

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

Yes, that's the correct behavior for inside labels, and it's basically the same thing we do for pies.

But there were two additional issues in my comment:

  • for outside labels, shall we do the same thing? ie rotate the bar 90 degrees when it would allow us to enlarge the scaling?
  • for inside labels, once we've figured out the maximum scaling, can we push the label over near the end of the bar rather than the middle?

Copy link
Owner Author

Choose a reason for hiding this comment

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

@alexcjohnson I've just realised that my implementation for outside labels doesn't do what I said 😞 . How do you think it should be? Shall I rotate it like in the case of inside labels?


👍 Yes, I'll push the labels close to the bar ends. One question: at the moment I'm using a 1px padding, shall I increase it (the padding in the ggplot2 example looks too much, though)?

Choose a reason for hiding this comment

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

Shall I rotate it like in the case of inside labels?

Yes, I think so. One concern with this (it's already a concern but will get worse when we rotate the text) is whether we need to include that label in autorange (via Axes.expand and its ppadplus and ppadminus options). That could get awfully annoying though, as you'd have to make the outside text elements available for sizing during setPositions rather than plot, but then you still need to know the bar width to tell whether to rotate or not... ugh. @etpinard thoughts?

1px padding, shall I increase it (the padding in the ggplot2 example looks too much, though)?

Agreed about ggplot2. For hover text we use 3px - how about that?

@@ -299,7 +299,7 @@ exports.cleanData = function(data, existingData) {
trace.scene = Plots.subplotsRegistry.gl3d.cleanId(trace.scene);
}

if(!Registry.traceIs(trace, 'pie')) {
if(!Registry.traceIs(trace, 'pie') && !Registry.traceIs(trace, 'bar')) {
Copy link

Choose a reason for hiding this comment

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

no need to clean (plotly.js lingo for backward-compatible mappings) attribute that have never been released.

Copy link
Owner Author

Choose a reason for hiding this comment

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

That's what this line change is meant for; i.e. to exclude bar's textposition from those mappings.

Copy link

Choose a reason for hiding this comment

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

Oops. You're right. My mistake. 😄

if(!isNumeric(bar.s)) continue;

var isOutmostBar = ((bar.b + bar.s) === sieve.get(bar.p, bar.s));
if(isOutmostBar) bar._outmost = true;
Copy link

Choose a reason for hiding this comment

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

nicely done here.

@@ -179,6 +179,24 @@ function setGroupPositionsInStackOrRelativeMode(gd, pa, sa, calcTraces) {

// set bar bases and sizes, and update size axis
stackBars(gd, sa, sieve);

// flag the outmost bar (for text display purposes)
for(var i = 0; i < calcTraces.length; i++) {
Copy link

@etpinard etpinard Nov 8, 2016

Choose a reason for hiding this comment

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

We don't need to run this loop if textposition: 'none', correct?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Last night, you made me hesitate! But after further thought, here's the correct reasoning.

Bars with textposition: 'none' are still visible (only the bar text is hidden) and thus they can be the outmost bar.

@@ -0,0 +1,23 @@
{
"data":[
Copy link

@etpinard etpinard Nov 8, 2016

Choose a reason for hiding this comment

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

@n-riesco general comment: would mind adding the text attributes and friends to the existing mocks you added for width / offset / base? We have over 300 image mocks now - which take about 2 and 1/2 minute to tests. It's time to be more meticulous when adding new mocks.

Copy link
Owner Author

Choose a reason for hiding this comment

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

I should've thought of that.

expect(pathBB.left).not.toBeGreaterThan(textBB.left);
expect(textBB.right).not.toBeGreaterThan(pathBB.right);
expect(pathBB.top).not.toBeGreaterThan(textBB.top);
expect(textBB.bottom).not.toBeGreaterThan(pathBB.bottom);
Copy link

Choose a reason for hiding this comment

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

very nice here.

Asserting getBoundingClientRect return values would be very susceptible to cross-browser cross-env discrepancies.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Since I've used Drawing.bBox(node) to compute the location of bar texts and Drawing.bBox(node) uses getBoundingClientRect this shouldn't be an issue.

I noticed, however, in certain cases, a rounding error that would cause the bar text to be negligibly larger than computed. For that reason, and also because it looks neater, I added some padding to bar texts. See here and here.

@etpinard
Copy link

etpinard commented Nov 8, 2016

all these attributes accept arrays

very nice

default textposition is none

yes 👍

if textposition was set to auto, the inside options is preferred over outside

I vote 👍 on that

if outside was requested, but the bar is stacked under another bar, then the text is drawn inside
if inside was requested, but the bar is too small, then no text is drawn

answered in #4 (review)

@n-riesco I'm pretty excited about this. Once again, you'll make a bunch of users happy!

* With the new criteria, bar texts are drawn inside bars except in those
  cases that would require scaling down.
* Added rotated and shrunk inside text.

* Shortened outside texts.
* Inside texts are rotated only if the rotation helps shrink the text as
  little as possible.

* Outside texts are rotated only if the rotation doesn't leave the text
  perpendicular to the bar.
* This change ensures inside texts can be drawn, even inside bars
  smaller than the text padding.
* Updated mock bar_attrs_relative to test the bar test functionality.
* Updated mock bar_attrs_group_norm to test the bar text functionality.
* Updated the jasmine tests to test whether outside bars are correctly
  identified when `barnorm: 'percent'`.
* Reduced font size of text "Three" to ensure the baseline image also
  fits the text inside the bar.
* Set outside font size to 32 to force rotation. This change will test
  that horizontal bars rotate the bar text clockwise or anti-clockwise
  as appropriate.
* Fixed bug computing the normalisation of bar plots in group mode.

* The bug was caused by calling `sieve.put` with bar sizes that didn't
  take into account the bar base.

* Bar plots in stack and relative mode weren't affected by this bug,
  because traces that set a bar base are excluded from the
  stack/relative mode.
@etpinard
Copy link

@n-riesco great. I'll give this a look in the next few hours. Thanks!

return transformTranslate + transformScale + transformRotate;
}

function getText(trace, index) {

Choose a reason for hiding this comment

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

Great. Nice and clear.

This is pretty much on bar with the Drawing does for scatter traces. So 👍

That said, I believe (I haven't tried yet) that some of this logic could be moved to the calc (which is called even less often than plot and supplyDefaults). But, that will be for a different PR.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@etpinard Is there anything in Drawing that you think I should've reused?

Choose a reason for hiding this comment

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

Drawing is pretty messy at the moment. Though it would be nice to merge the text-drawing logic from scatter, pie and bar together at some point. No action required for this iteration.

return value;
}

function coerceString(attributeDefinition, value, defaultValue) {

Choose a reason for hiding this comment

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

Can you simply reuse Lib.coerce here?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I couldn't do it when textfont accepted an array, but now I should be able to.

Choose a reason for hiding this comment

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

⬆️ blocking before making a PR to plotly.js

Copy link
Owner Author

@n-riesco n-riesco Nov 16, 2016

Choose a reason for hiding this comment

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

@etpinard After some sleep, I remembered that the reason why I introduced those functions is that I don't know how to use Lib.coerce to coerce array elements. Even if something like Lib.coerce('textfont.family[' + index + ']', dflt) works, iterating an array like this must be awfully inefficient.

Would it be OK for me to modify src/lib/corce.js and move coerceString to a coerce field and update the corresponding coerceFunction to use this new coerce field?

Choose a reason for hiding this comment

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

iterating an array like this must be awfully inefficient.

Very true. That's why we don't use Lib.coerce on array items usually. You probably noticed that Drawing doesn't have any coerce statements.

For what it's worth, here's an example of how to get it to work with array under info_array.

So, I'll leave it up to you. I'm ok with the current solution. But ideally, we should move all array-item casting to the calc step - which can wait for a refactor PR.

"x":[3,2,1,0],
"text":0.5,
"textposition":"outside",
"outsidetextfont": {"size":32},

Choose a reason for hiding this comment

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

Hmm. Looks like that "size": 32 isn't reflected on the baseline image.

Copy link
Owner Author

@n-riesco n-riesco Nov 15, 2016

Choose a reason for hiding this comment

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

@etpinard This is on purpose to force a rotation in all the outside labels. If the font is small enough, the label can be drawn without rotation.

You can play with the size to see what I mean.

Choose a reason for hiding this comment

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

Got it. Thanks for the info!

@@ -2,27 +2,37 @@
"data":[
{
"width":[1,0.8,0.6,0.4],
"text":[1,2,3333333333,4],
"textposition":"outside",
Copy link

@etpinard etpinard Nov 15, 2016

Choose a reason for hiding this comment

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

If I understand correctly, only the outer most bar can have outside text?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Yes, that's correct. Is there anything wrong?

Choose a reason for hiding this comment

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

Nothing wrong. Just double checking. 👍

@@ -623,13 +871,13 @@ describe('A bar plot', function() {
[1, 2, 3, 4], [1, 2, 3, 4]]);
assertPointField(cd, 'y', [
[1, 2, 3, 4], [4, 4, 4, 4],
[-1, -3, -2, -4], [4, -4, -5, -6]]);
[-1, -3, -2, -4], [4, -3.25, -5, -6]]);

Choose a reason for hiding this comment

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

why?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I wanted to have a small negative bar, so that the label "outside text" doesn't fit inside the bar and has to be drawn below the bar (as opposed to the positive bar case that has outside texts drawn above the bar).

Choose a reason for hiding this comment

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

got it, thanks!

@@ -674,6 +922,88 @@ describe('A bar plot', function() {
done();
});
});

it('should coerce text-related attributes', function(done) {

Choose a reason for hiding this comment

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

Nice test. Can you make sure that Plotly.restyle works with the new attributes too?

Choose a reason for hiding this comment

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

⬆️ blocking before making a PR to plotly.js

"y":[1,2,3,4],
"x":[1,2,3,4],
"type":"bar"
}, {
"width":[0.4,0.6,0.8,1],
"text":["Three",2,"outside text",0],

Choose a reason for hiding this comment

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

Two questions here:

  1. why is "outside text" outside? Wouldn't it be just as big on the inside?
  2. Where did "0" go? Even though the bar has no size, because it's the last bar we could show it where the last bar would have been (and likewise, it's confusing that the "4" from trace 0 shows up on the outside, it feels like only trace 1 should be able to have a label there.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Why is "outside text" outside? Wouldn't it be just as big on the inside?

The way I understood the criteria for auto is that if it doesn't fit inside, it goes outside (no matter if shrinking is required to draw it outside).

Where did "0" go?

0-size bars aren't even included in gd.calcdata

it feels like only trace 1 should be able to have a label there

I think this feeling is specific to this case, because trace[1] only has bars with positive sizes. If trace[1][3] was -1 instead of 0, then the bar and the label would be drawn on the negative side.

Choose a reason for hiding this comment

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

The way I understood the criteria for auto is that if it doesn't fit inside, it goes outside (no matter if shrinking is required to draw it outside).

Yeah, that's probably what I said :) But would you agree that if the shrinking is the same either way then it should stay inside?

0-size bars aren't even included in gd.calcdata

OK cool - yes, definitely an edge case, lets leave this one as it is.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@alexcjohnson OK, I'll change auto:

  • if it fits inside, the label will be drawn inside
  • if it doesn't, the label will be drawn wherever is larger

"type":"bar"
}, {
"base":[7,6,5,4],
"x":[1,2,3,4],
"text":0.5,

Choose a reason for hiding this comment

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

It's confusing to read this plot with the different text labels rotated different directions. I'd rotate them only clockwise (if they're going to rotate)

Copy link
Owner Author

Choose a reason for hiding this comment

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

How about we don't rotate the labels at all and let the user decide?

Something like this:

    textrotation: {
        valType: 'number',
        dflt: null,
        arrayOk: true,
        role: 'info',
        description: [
            'Rotates the bar text (in clockwise degrees).'
        ].join(' ')
    },

Copy link

@etpinard etpinard Nov 15, 2016

Choose a reason for hiding this comment

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

Adding textrotation isn't a bad idea at all. I'm sure some users will want to rotate their labels to 45 degrees.

Choose a reason for hiding this comment

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

That could be an option - for conformity with tick labels it should be textangle - though offhand I'd say it opens more questions than it answers. For example, you might want 45 degrees or something for text on vertical bars in order to make the labels more readable, but have the labels expand into the space of the next bar(s) - similar to how angles are used on tick labels. But then because these labels start from different vertical points, making it so they don't overlap becomes challenging. I would leave this out for now, and then when we do add it, this will override the "auto" rotation that we determine now.

So then thinking a little more about what this auto behavior should be - basically I feel like text should be horizontal unless it can be less shrunken being vertical - similar to the inside/outside criterion. I think that's already what you're doing for vertical bars, but for horizontal it looks like you're defaulting to vertical text when I think 99% of the time it should be horizontal, and then in those cases that you do decide to rotate the text, it should only turn 90 degrees clockwise.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@alexcjohnson For vertical bars, the criteria of maximising label size, would cause the outside text labels in the plot below to be vertical:

image

Would that be OK?

text: scatterAttrs.text,

textposition: {

Choose a reason for hiding this comment

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

@n-riesco what are your thoughts on adding a textinfo attribute (similar to the pie) down the road?

The default value would be 'text' which corresponds the value linked to the text attribute. Another possiblevalues could be 'total' (i.e. the total in a stack barmode).

Copy link
Owner Author

Choose a reason for hiding this comment

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

What would be the meaning of 'total' for inner bars? no label? the bar size? the cum sum up to that bar?

Choose a reason for hiding this comment

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

What would be the meaning of 'total' for inner bars?

I'm thinking 'total' would draw only one text item per bar group. But yeah, this would open up a cross-trace problem. Not so fun.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@etpinard I think this is doable. I can compute the total at the some point I determine which bars are the outmost bars.

Choose a reason for hiding this comment

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

I think this is doable

Great. That's all I wanted to here for now. No action required in this iteration.

dflt: 'none',
arrayOk: true,
description: [
'Specifies the location of the `textinfo`.'

Choose a reason for hiding this comment

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

@n-riesco this would be a great place to add info about the inside / outside algorithm you implemented.

Choose a reason for hiding this comment

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

⬆️ blocking before making a PR to plotly.js

* Tested that `textposition` can also be restyled.
* Changed bar texts to "50%" to ensure the bounding box width is larger
  than its height (unfortunately, this wasn't case when the baseline
  images were generated; the height of the bounding box includes some
  white space).

* Swapped the `texposition` of both traces so that illustrates the least
  favourable case for clockwise rotation.
@n-riesco
Copy link
Owner Author

@alexcjohnson I've updated the rotation algorithm. See the images below:

  • now rotations are always 90 degrees clockwise
  • in the case of outside labels, I'm still preventing rotation if this results in a label being perpendicular to the bar
  • I've changed the labels in the first plot from "0.5" to "50%" to ensure that the text width is larger than the height (unfortunately, this wasn't case in the baseline images, and it was hard to reason why the inside labels had been rotated)
  • I've also swapped textposition in the first plot, so that trace1 shows rotated outside labels (the case I think is the least favourable for clockwise rotation)

Questions before I continue:

  • do you like the results of the new criteria?
  • do you still think that I should change the criteria for the 'auto' mode?

@alexcjohnson
Copy link

@n-riesco the rotation algorithm is better but I still think for both horizontal and vertical bars the first choice should be horizontal text, and you only rotate it if horizontal is squished and vertical is (less) squished. That means that really the only time you would end up rotating text on horizontal bars is when inside is explicitly specified and the bar size is small enough that it's thinner than a square.

And again, in bar_attrs_relative I don't see why "outside text" in trace 1 is outside the orange bar, it looks like it would be just as big inside the bar, wouldn't it?

@n-riesco
Copy link
Owner Author

@alexcjohnson Thx, that's helped clear my mind a lot.

* Don't rotate outside labels.
* 'auto' will draw a label inside a bar if larger than drawn outside.
* Updated bar tests to account for the change in criteria for
  `textposition:'auto'`.
@n-riesco
Copy link
Owner Author

These are the latest results using these criteria:

  • auto only draws outside if the result is larger than inside
  • inside labels are rotated if it prevents excesive shrinkage
  • outside labels are never rotated
  • rotation are always 90 degrees clockwise

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

Successfully merging this pull request may close these issues.

3 participants