-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Better polar setConvert + a few misc polar touch ups #2895
Conversation
- 🔪 one obsolete comment - use trace.orientation instead using same x/y logic as in supplyDefaults.
- introduce 'g'eomtric coordinate system, that handles angular rotation / directrion and radial range shifts - add 't'icks coord system for angular axes tick vals - add setGeometry (which sets the c2g converters) - make 'c' linear theta coordinates be in radians, eliminating trace.thetaunit deps after 'calc'
... which is on-par with fullLayout.polar.angularaxis
- where the dtheta default is the ax.period divided by the trace length (given by the length of the 'other' coordinate).
... until we actually implement it in #2887
- follow-up from #2737
|
||
var xa = Axes.getFromId(gd, trace.xaxis || 'x'), | ||
ya = Axes.getFromId(gd, trace.yaxis || 'y'), | ||
orientation = trace.orientation || ((trace.x && !trace.y) ? 'h' : 'v'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch!
src/plots/polar/set_convert.js
Outdated
* + for category axes: | ||
* - d2c (in calc) just like for cartesian axes | ||
* - c2g (in plot) category indices -> 'geometric' radians | ||
* - t2g (in updateAngularAxis) 'tick' value (as category indices) -> 'geometric' radians |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be easier for me to understand if instead of describing t2g
etc, just describe t
and g
(and c
and d
if they're different from cartesian) for the linear and category cases)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(like
plotly.js/src/plots/cartesian/set_convert.js
Lines 35 to 50 in def6aa5
* d: data, in whatever form it's provided | |
* c: calcdata: turned into numbers, but not linearized | |
* l: linearized - same as c except for log axes (and other nonlinear | |
* mappings later?) this is used when we need to know if it's | |
* *possible* to show some data on this axis, without caring about | |
* the current range | |
* p: pixel value - mapped to the screen with current size and zoom | |
* r: ranges, tick0, and annotation positions match one of the above | |
* but are handled differently for different types: | |
* - linear and date: data format (d) | |
* - category: calcdata format (c), and will stay that way because | |
* the data format has no continuous mapping | |
* - log: linearized (l) format | |
* TODO: in v2.0 we plan to change it to data format. At that point | |
* shapes will work the same way as ranges, tick0, and annotations | |
* so they can use this conversion too. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
latest attempt in -> 4c11c84
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much clearer - thanks!
src/plots/polar/set_convert.js
Outdated
var rot = deg2rad(ax.rotation); | ||
|
||
ax.rad2g = function(v) { return dir * v + rot; }; | ||
ax.g2rad = function(v) { return (v - rot) / dir; }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in slack, seems like we can 🔪 ax.rad2*
and ax.*2rad
entirely now, so we don't need to think about the difference between rad
and g
to understand all this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in f5bceda
- ?2rad and rad2? methods are now gone - ax._period is gone
* - this.yaxis | ||
* setup so that polar traces can reuse plot methods of Cartesian traces | ||
* which mostly rely on 2pixel methods (e.g ax.c2p) | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! 📚
src/plots/polar/polar.js
Outdated
@@ -257,8 +257,6 @@ proto.updateLayout = function(fullLayout, polarLayout) { | |||
_this.angularAxis = _this.mockAxis(fullLayout, polarLayout, angularLayout, { | |||
_axislayer: layers['angular-axis'], | |||
_gridlayer: layers['angular-grid'], | |||
// angular axes need *special* logic | |||
_id: 'angular', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for reference, here's where _id
gets set to angularaxis
axOut._id = axOut._name = axName; |
src/plots/cartesian/axes.js
Outdated
@@ -554,7 +554,7 @@ axes.calcTicks = function calcTicks(ax) { | |||
|
|||
// If same angle over a full circle, the last tick vals is a duplicate. | |||
// TODO must do something similar for angular date axes. | |||
if(ax._id === 'angular' && Math.abs(rng[1] - rng[0]) === 360) { | |||
if(ax._id === 'angularaxis' && Math.abs(rng[1] - rng[0]) === 360) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This gets a lot of reuse... perhaps function isAngular(axid) { return axid === 'angularaxis'; }
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call -> 0c517d1
src/plots/cartesian/axes.js
Outdated
@@ -1682,7 +1682,7 @@ axes.doTicksSingle = function(gd, arg, skipTitle) { | |||
var valsClipped = vals.filter(clipEnds); | |||
|
|||
// don't clip angular values | |||
if(ax._id === 'angular') { | |||
if(ax._id === 'angularaxis') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not new in this PR, but I think you have an axid
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
went one step further in 0c517d1
'By default, the `dtheta` step equals the subplot\'s period divided', | ||
'by the length of the `r` coordinates.' | ||
].join(' ') | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One of the most underrated features, IMHO. Thanks for adding it!
src/traces/scattergl/index.js
Outdated
@@ -302,6 +302,8 @@ function sceneUpdate(gd, subplot) { | |||
} | |||
if(scene.scatter2d) { | |||
clearViewport(scene.scatter2d, vp); | |||
} else if(scene.line2d) { | |||
clearViewport(scene.line2d, vp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first arg to clearViewport
is just used to find the regl._gl
context... and so I guess the reason select2d
is off by itself is that select gets its own context separate from the main data context?
Might be clearer then to refactor this as something like
var anyComponent = scene.scatter2d || scene.line2d || (scene.glText || [])[0];
if(anyComponent) clearViewport(anyComponent, vp);
Which would also make it easier to add error2d
(can that ever be the only thing drawn?) and fill2d
to the lookup list, looks plausible that these also have bugs similar to #2888.
BTW, looking at what clearViewport
does - ie clears a rectangle from the context - what happens if two polar subplots have overlapping bounding boxes? Like if you made a hexagonal packing of polar subplots or you do something funny with sectors, like you make one large pac-man subplot eating a smaller one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens if two polar subplots have overlapping bounding boxes?
This happens -> #2797
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be clearer then to refactor this as something like
Absolutely -> c3044e0
add error2d (can that ever be the only thing drawn?)
Traces with error bar will always have either an active scene.scatter2d
or scene.line2d
.
BTW, looking at what clearViewport does - ie clears a rectangle from the context -
We should take a look at how much of a perf gain is to clear only a fraction of the canvas on pan/select. If that's negligible, then I'm thinking we should be keeping track of a graph-wide regl-draw-queue instead, always clear the whole canvas and draw all regl parts on pan/select. This should make this a lot more manageable. cc @dy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Traces with error bar will always have either an active
scene.scatter2d
orscene.line2d
.
Even if mode: 'none'
? That works fine for scatter
, and sometimes tries to work with scattergl
#2900
fill
works with mode: 'none'
too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bulletproofed in 75786e4
src/plots/polar/polar.js
Outdated
@@ -993,11 +993,13 @@ proto.updateRadialDrag = function(fullLayout, polarLayout) { | |||
var moduleCalcDataVisible = Lib.filterVisible(moduleCalcData); | |||
var _module = moduleCalcData[0][0].trace._module; | |||
var polarLayoutNow = gd._fullLayout[_this.id]; | |||
var isGL = Registry.traceIs(k, 'gl'); | |||
|
|||
if(isGL && _this._scene) _this._scene.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible to not have _this._scene
at this point if there's a gl
trace?
BTW this would be clearer with traceType
instead of k
.
I don't see a problem at the moment because there's only one gl
type that can be here, but for future reference seems like if we ever add another we'll have to make sure only the first one clears the scene.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for tip. Improvements in -> b81885d
test/jasmine/tests/polar_test.js
Outdated
@@ -1136,7 +1143,9 @@ describe('Test polar interactions:', function() { | |||
fig.layout.margin = {l: 50, t: 50, b: 50, r: 50}; | |||
|
|||
if(s.patch) s.patch(fig); | |||
nTraces = fig.data.length; | |||
nTraces = fig.data.reduce(function(acc, trace) { | |||
return (trace.type === 'scatterpolargl') ? ++acc : acc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, OK. I always find prefix ++
confusing, and perhaps because it encourages stuff like that reduce
also often comes out confusing to me. I realize it's better perf this way, but that's irrelevant to a test.
fig.data.filter(function(trace) { return trace.type === 'scatterpolargl'; }).length
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're right, filter is better: b4700ca
... that is move scene.clear() out of trace-type loop
Nice work! 💪 💃 |
This is a preliminary PR for
barpolar
.I dove back hard into the polar code in the past 10 days and I noticed a few hacks that were worth cleaning up before adding
barpolar
.Commit 3d8eef2 is the big one. In brief, it locks down what 'c'alc means for angular coordinates and adds a new coordinate system (I called it 'g'eometric) that depends on things that interactions can update (i.e.
radialaxis.range
andangularaxis.rotation
). I invite you to read thesetConvertPolar
docstring as well as commit 91064ca for more info. It's still not perfect: ideally we should try to factor things inAxes.doTicks
to no longer rely on 2 radial and 2 angular axis objects per polar subplots, but this feels like a step in the right direction.After that, this PR also:
r0/dr
andtheta0/dtheta
. I decided to compute thedtheta
default using the angular period and the number ofr
coordinates in the trace - which seems to give pretty user-friendly results and will be useful forbarpolar
.scatterpolargl
following-up on Enable text for scattergl trace #2737hoveron
from thescattergl
andscatterpolargl
attributes as discussed in Implementhoveron: 'fills'
for scattergl and scatterpolargl #2887 (comment)cc @alexcjohnson