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

Multi-axis-type sploms #2899

Merged
merged 13 commits into from
Aug 15, 2018
Merged

Multi-axis-type sploms #2899

merged 13 commits into from
Aug 15, 2018

Conversation

etpinard
Copy link
Contributor

This PR fixes axis tick rendering for categorical sploms For example, on master

Plotly.newPlot(gd, [{
  type: 'splom',
  dimensions: [{
    values: ['a','b','d']
  }, {
    values: ['a', 'b', 'c']
  }]
}])

renders as:

image

and add supports for sploms that generate axis of different types, see 9da5a1b for an example.

cc @alexcjohnson

- using trace._diag, this allows to support multiple axis type
  in splom-generated axes
... to make categorical axis ticks show up correctly
- note that axis type can be overwritten in layout
@etpinard etpinard added bug something broken feature something new status: reviewable labels Aug 13, 2018
@etpinard etpinard added this to the v1.40.0 milestone Aug 13, 2018
@@ -89,9 +89,10 @@ function setAutoType(ax, data) {
}
else if(d0.type === 'splom') {
var dimensions = d0.dimensions;
var diag = d0._diag;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Recall that _diag is:

// build list of [x,y] axis corresponding to each dimensions[i],
// very useful for passing options to regl-splom
var diag = traceOut._diag = new Array(dimLength);
// cases where showDiag and showLower or showUpper are false
// no special treatment as the xaxes and yaxes items no longer match
// the dimensions items 1-to-1
var xShift = !showDiag && !showLower ? -1 : 0;
var yShift = !showDiag && !showUpper ? -1 : 0;
for(i = 0; i < dimLength; i++) {
var dim = dimensions[i];
var xa = xaxes[i + xShift];
var ya = yaxes[i + yShift];
fillAxisStash(layout, xa, dim);
fillAxisStash(layout, ya, dim);
// note that some the entries here may be undefined
diag[i] = [xa, ya];
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

ah ok - a little confusing as usually xa is a full axis object, an id would be xId or something... but anyway nice fix!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cleaned up in -> f3c73db

"r": 30,
"t": 30
},
"yaxis2": {"type": "category"},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This here might be tricky to find for users as e.g. nothing indicates that yaxis2 corresponds to the boolean data array.

Perhaps we could include this in the trace dimensions[i] containers?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps we could include this in the trace dimensions[i] containers?

That's a great idea - dimension.type = 'category' to override the autotype for both axes. That would certainly reduce (though not eliminate) the chance of mismatches like #2899 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went with dimensions[i].axis.type in 39b71bb

I'm thinking the extra container might make it cleaner if we add more "splom-generated-axis" settings e.g. I suspect some users would want to set dimensions[i].axis.showgrid: false

Copy link
Collaborator

Choose a reason for hiding this comment

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

haha exactly my rationale in #2899 (comment) - I should read all your comments before making my own. Anyway, love it.

makeCalcdata(ya, dim);
if(xa && xa.type === 'category') {
xa._categories = ya._categories.slice();
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

How could you get here? if xa exists you'll skip the else if(ya) block entirely.

But this raises an interesting (if possibly nonsensical) point - what if xa.type !== ya.type? I guess this could happen if one is numeric or date and the other is categorical, or if one is linear and the other is log. If the answer is "all hell breaks loose", which wouldn't surprise me, it may be appropriate to just log a warning and hide that dimension entirely. That seems like an abuse of the concept of a splom.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How could you get here? if xa exists you'll skip the else if(ya) block entirely.

Good point. I wrote that up to preserve symmetry, but didn't bother checking. Thanks!

what if xa.type !== ya.type? I guess this could happen if one is numeric or date and the other is categorical, or if one is linear and the other is log.

I don't think this can happen on graphs with only 1 splom trace and no ax.type set in the layout. That's because (after this PR's 1st commit dd563bb), both x and y axes use the same data array in the axis autotype routine.

Scenarios where xa.type !== ya.type can happen if,

  • a trace before the splom trace in gd.data autotyped the axes
  • a users sets ax.type in the layout

which are things that don't happen by accident.

So, I yeah I think skipping that dimension entirely makes the most sense here. Thanks for bringing this up!

Copy link
Collaborator

Choose a reason for hiding this comment

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

a users sets ax.type in the layout
... [doesn't] happen by accident.

Hmm could happen in the editor though. @nicolaskruchten @VeraZab not sure how best to handle this. If we add dimension.type we should steer people there somehow when there's a splom on that axis. But if they still set an axis type directly would we want to find the corresponding opposite axis in the splom and set it to match, to avoid this error?

Copy link
Contributor

Choose a reason for hiding this comment

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

hoo boy. Well right now we don't support SPLOMs yet, and I'm thinking that when we do we should lock down axes/subplots quite tightly so that people can't break them too much :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Skipping dims with axes of conflicting types in b3d27bd

... which simplifies logic downstream
... with other sub-supply-defaults routines
.. to more easily set axis type on splom-generated axes
@@ -61,6 +61,8 @@ function dimensionDefaults(dimIn, dimOut) {

if(!(values && values.length)) dimOut.visible = false;
else coerce('visible');

coerce('axis.type');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops. I committed the new lines in splom/attributes.js in 9039a8e by accident. My bad.

@@ -16,11 +16,13 @@
},
{
"label": "bool",
"values": [false, true, true, true, false, true, false, false, false, true]
"values": [false, true, true, true, false, true, false, false, false, true],
"axis": {"type": "category"}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, that's way cleaner! 🎉

Good call making an axis container, I can see us wanting to allow other axis settings (gridlines, tick spacing, tick text...) to be set at the dimension level in the future.

I started thinking, since it's (generally) going to apply this to 2 axes, perhaps it should be called axes... but then that (elsewhere in the schema) implies a list of axes rather than axis properties. So I think it's good the way you have it.

expect(fullLayout.yaxis2.type).toBe('category');
});

it('axis type setting should be skipped when dimension is not visible', function() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

agreed, good call.


var cd = gd.calcdata[0][0];

expect(cd.t._scene.matrixOptions.data).toBeCloseTo2DArray([[2, 1, 2]]);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is good, but I have various questions about what happens downstream from calc with the missing dimension(s)... like, cdata and ldata arrays don't match up with the dimension array anymore, does this get handled correctly in plotting? The next commit (visibleDims) seems like it assuages my concerns a bit, but would you mind including a mismatched-type dimension (somewhere in the middle of dimensions) in a mock so we can see it all the way to the end?

Copy link
Contributor Author

@etpinard etpinard Aug 15, 2018

Choose a reason for hiding this comment

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

Good call, done in -> 390f292

@@ -59,16 +59,17 @@ function calc(gd, trace) {
xa = AxisIDs.getFromId(gd, trace._diag[i][0]);
ya = AxisIDs.getFromId(gd, trace._diag[i][1]);

// if corresponding x & y axes don't have matching types, skip dim
if(xa && ya && xa.type !== ya.type) continue;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems likely to confuse people if they encounter this issue... do you want to include a Log message, or perhaps find a way to surface this in Plotly.validate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As this is part of the calc step (which is outside the scope of Plotly.validate) and that we can't easily move this to the defaults step (as axis defaults are coerced after trace defaults), I chose to only log something in 1823904

Note that I chose I was first planning on using Lib.warn instead of Lib.log, but currently the image server bails whenever a mock results in a Lib.warn, so the mock added in 390f292 would fail to generate an image. I hope you're ok with just a Lib.log here.

.. in case (e.g. for splom) they need to lookup a few things
   in fullData and calcdata
... when restyling dragmode to non-selection modes.

    Previously, select -> dblclick -> pan lead to a duplication of
    rendered pts.
.then(function() { return Plotly.relayout(gd, 'dragmode', 'pan'); })
.then(function() {
_assert('under dragmode:pan with NO active selection', {
updateCnt: 1, // to clear off 1 matrixTrace
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before this commit, this

peek 2018-08-15 12-46

was happening.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice find, thanks for including the fix here!

.then(function() { return Plotly.relayout(gd, 'dragmode', 'select'); })
.then(function() {
_assert('under dragmode:select', {
updateCnt: 3, // updates positions, viewport and style in 3 calls
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Recall that relayout(gd, 'dragmode', 'select') results in a plot edit here because of:

if((fullLayout._has('scatter-like') && fullLayout._has('regl')) &&
(ai === 'dragmode' &&
(vi === 'lasso' || vi === 'select') &&
!(vOld === 'lasso' || vOld === 'select'))
) {
flags.plot = true;
}

... to avoid confusing with i in dimension loop in
    outer-scope.
"xref": "paper", "yref": "paper",
"xanchor": "right", "yanchor": "top",
"x": 1, "y": 1,
"text": "Should <b>not</b> see points in the \"bool\" dimension<br>as it has conflicting axis types",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Great mock, thanks!

@alexcjohnson
Copy link
Collaborator

Excellent, this is really going to help robustness and usability for splom traces. 💃

@etpinard etpinard merged commit 7bba266 into master Aug 15, 2018
@etpinard etpinard deleted the categorical-sploms branch August 15, 2018 20:27
@etpinard etpinard mentioned this pull request Oct 2, 2018
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something broken feature something new
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants