Skip to content

Commit 38c6ad8

Browse files
committed
use drawTIcks, drawGrid and drawLabels to draw polar axes
- call full updateRadialAxis on radial drag and call full updateAngularAxis on angular drag. - no need to nest grid lines into extra <g> (that was an artifact from doTicksSingle. - ... more cleanup to come.
1 parent 73a38fd commit 38c6ad8

File tree

3 files changed

+116
-52
lines changed

3 files changed

+116
-52
lines changed

src/plots/cartesian/axes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2425,6 +2425,11 @@ axes.makeLabelFns = function(ax, shift, angle) {
24252425
labelStandoff += 0.2 * ax.tickfont.size;
24262426
}
24272427

2428+
// TODO get rid of!
2429+
ax._pad = pad;
2430+
ax._labelStandoff = labelStandoff;
2431+
ax._labelShift = labelShift;
2432+
24282433
var out = {};
24292434
var x0, y0, ff, flipIt;
24302435
if(axLetter === 'x') {
@@ -2620,6 +2625,7 @@ axes.drawLabels = function(gd, ax, opts) {
26202625

26212626
ax._tickLabels = tickLabels;
26222627

2628+
// TODO ??
26232629
if(isAngular(ax)) {
26242630
tickLabels.each(function(d) {
26252631
d3.select(this).select('text')

src/plots/polar/polar.js

Lines changed: 107 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ var Lib = require('../../lib');
1616
var Color = require('../../components/color');
1717
var Drawing = require('../../components/drawing');
1818
var Plots = require('../plots');
19+
var Axes = require('../../plots/cartesian/axes');
1920
var setConvertCartesian = require('../cartesian/set_convert');
2021
var setConvertPolar = require('./set_convert');
2122
var doAutoRange = require('../cartesian/autorange').doAutoRange;
22-
var doTicksSingle = require('../cartesian/axes').doTicksSingle;
2323
var dragBox = require('../cartesian/dragbox');
2424
var dragElement = require('../../components/dragelement');
2525
var Fx = require('../../components/fx');
@@ -65,7 +65,7 @@ function Polar(gd, id) {
6565
.attr('class', id);
6666

6767
// unfortunately, we have to keep track of some axis tick settings
68-
// so that we don't have to call doTicksSingle with its special redraw flag
68+
// as polar subplots do not implement the 'ticks' editType
6969
this.radialTickLayout = null;
7070
this.angularTickLayout = null;
7171
}
@@ -141,11 +141,9 @@ proto.updateLayers = function(fullLayout, polarLayout) {
141141
break;
142142
case 'radial-grid':
143143
sel.style('fill', 'none');
144-
sel.append('g').classed('x', 1);
145144
break;
146145
case 'angular-grid':
147146
sel.style('fill', 'none');
148-
sel.append('g').classed('angularaxis', 1);
149147
break;
150148
case 'radial-line':
151149
sel.append('line').style('fill', 'none');
@@ -167,14 +165,15 @@ proto.updateLayers = function(fullLayout, polarLayout) {
167165
*
168166
* - this.radialAxis
169167
* extends polarLayout.radialaxis, adds mocked 'domain' and
170-
* few other keys in order to reuse Cartesian doAutoRange and doTicksSingle,
168+
* few other keys in order to reuse Cartesian doAutoRange and the Axes
169+
* drawing routines.
171170
* used for calcdata -> geometric conversions (aka c2g) during the plot step
172171
* + setGeometry setups ax.c2g for given ax.range
173172
* + setScale setups ax._m,ax._b for given ax.range
174173
*
175174
* - this.angularAxis
176175
* extends polarLayout.angularaxis, adds mocked 'range' and 'domain' and
177-
* a few other keys in order to reuse Cartesian doTicksSingle,
176+
* a few other keys in order to reuse the Axes drawing routines.
178177
* used for calcdata -> geometric conversions (aka c2g) during the plot step
179178
* + setGeometry setups ax.c2g given ax.rotation, ax.direction & ax._categories,
180179
* and mocks ax.range
@@ -247,8 +246,6 @@ proto.updateLayout = function(fullLayout, polarLayout) {
247246
var cyy = _this.cyy = cy - yOffset2;
248247

249248
_this.radialAxis = _this.mockAxis(fullLayout, polarLayout, radialLayout, {
250-
_axislayer: layers['radial-axis'],
251-
_gridlayer: layers['radial-grid'],
252249
// make this an 'x' axis to make positioning (especially rotation) easier
253250
_id: 'x',
254251
// convert to 'x' axis equivalent
@@ -261,8 +258,6 @@ proto.updateLayout = function(fullLayout, polarLayout) {
261258
});
262259

263260
_this.angularAxis = _this.mockAxis(fullLayout, polarLayout, angularLayout, {
264-
_axislayer: layers['angular-axis'],
265-
_gridlayer: layers['angular-grid'],
266261
side: 'right',
267262
// to get auto nticks right
268263
domain: [0, Math.PI],
@@ -311,12 +306,7 @@ proto.mockAxis = function(fullLayout, polarLayout, axLayout, opts) {
311306
var commonOpts = {
312307
// to get _boundingBox computation right when showticklabels is false
313308
anchor: 'free',
314-
position: 0,
315-
_pos: 0,
316-
// dummy truthy value to make doTicksSingle draw the grid
317-
_counteraxis: true,
318-
// don't use automargins routine for labels
319-
automargin: false
309+
position: 0
320310
};
321311

322312
var ax = Lib.extendFlat(commonOpts, axLayout, opts);
@@ -392,18 +382,18 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) {
392382
// rotate auto tick labels by 180 if in quadrant II and III to make them
393383
// readable from left-to-right
394384
//
395-
// TODO try moving deeper in doTicksSingle for better results?
385+
// TODO try moving deeper in Axes.drawLabels for better results?
396386
if(ax.tickangle === 'auto' && (a0 > 90 && a0 <= 270)) {
397387
ax.tickangle = 180;
398388
}
399389

400390
// easier to set rotate angle with custom translate function
401-
ax._transfn = function(d) {
391+
var transFn = function(d) {
402392
return 'translate(' + (ax.l2p(d.x) + innerRadius) + ',0)';
403393
};
404394

405395
// set special grid path function
406-
ax._gridpath = function(d) {
396+
var gridPathFn = function(d) {
407397
return _this.pathArc(ax.r2p(d.x) + innerRadius);
408398
};
409399

@@ -415,14 +405,50 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) {
415405

416406
if(hasRoomForIt) {
417407
ax.setScale();
418-
doTicksSingle(gd, ax, true);
408+
409+
var vals = Axes.calcTicks(ax);
410+
var valsClipped = Axes.clipEnds(ax, vals);
411+
var labelFns = Axes.makeLabelFns(ax, 0);
412+
413+
var sideOpposite = {x: 'top', y: 'right'}[ax._id];
414+
var tickSign = [-1, 1, ax.side === sideOpposite ? 1 : -1];
415+
if((ax.ticks !== 'inside') === (ax._id === 'x')) {
416+
tickSign = tickSign.map(function(v) { return -v; });
417+
}
418+
419+
Axes.drawTicks(gd, ax, {
420+
// TODO right? No need to clip here ever?
421+
vals: vals,
422+
layer: layers['radial-axis'],
423+
path: Axes.makeTickPath(ax, 0, tickSign[2]),
424+
transFn: transFn
425+
});
426+
427+
Axes.drawGrid(gd, ax, {
428+
vals: valsClipped,
429+
layer: layers['radial-grid'],
430+
path: gridPathFn,
431+
transFn: transFn
432+
});
433+
434+
Axes.drawLabels(gd, ax, {
435+
vals: vals,
436+
layer: layers['radial-axis'],
437+
shift: 0,
438+
transFn: transFn,
439+
labelXFn: labelFns.labelXFn,
440+
labelYFn: labelFns.labelYFn,
441+
labelAnchorFn: labelFns.labelAnchorFn,
442+
skipTitle: true
443+
});
419444
}
420445

421446
// stash 'actual' radial axis angle for drag handlers (in degrees)
422447
var angle = _this.radialAxisAngle = _this.vangles ?
423448
rad2deg(snapToVertexAngle(deg2rad(radialLayout.angle), _this.vangles)) :
424449
radialLayout.angle;
425450

451+
// TODO no more "draw-and-then-tweak" !!!
426452
var trans = strTranslate(cx, cy) + strRotate(-angle);
427453

428454
updateElement(
@@ -431,6 +457,7 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) {
431457
{transform: trans}
432458
);
433459

460+
// TODO !!
434461
// move all grid paths to about circle center,
435462
// undo individual grid lines translations
436463
updateElement(
@@ -514,16 +541,8 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
514541
ax.dtick = rad2deg(ax.dtick);
515542
}
516543

517-
// Use tickval filter for category axes instead of tweaking
518-
// the range w.r.t sector, so that sectors that cross 360 can
519-
// show all their ticks.
520-
if(ax.type === 'category') {
521-
ax._tickFilter = function(d) {
522-
return Lib.isAngleInsideSector(t2g(d), _this.sectorInRad);
523-
};
524-
}
525-
526-
ax._transfn = function(d) {
544+
// TODO break up into two functions!
545+
var transFn = function(d) {
527546
var sel = d3.select(this);
528547
var hasElement = sel && sel.node();
529548

@@ -541,17 +560,18 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
541560
return out;
542561
};
543562

544-
ax._gridpath = function(d) {
563+
var gridPathFn = function(d) {
545564
var rad = t2g(d);
546565
var cosRad = Math.cos(rad);
547566
var sinRad = Math.sin(rad);
548567
return 'M' + [cx + innerRadius * cosRad, cy - innerRadius * sinRad] +
549568
'L' + [cx + radius * cosRad, cy - radius * sinRad];
550569
};
551570

571+
Axes.makeLabelFns(ax, 0);
552572
var offset4fontsize = (angularLayout.ticks !== 'outside' ? 0.7 : 0.5);
553573

554-
ax._labelx = function(d) {
574+
var labelXFn = function(d) {
555575
var rad = t2g(d);
556576
var labelStandoff = ax._labelStandoff;
557577
var pad = ax._pad;
@@ -564,7 +584,7 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
564584
return offset4tx + offset4tick;
565585
};
566586

567-
ax._labely = function(d) {
587+
var labelYFn = function(d) {
568588
var rad = t2g(d);
569589
var labelStandoff = ax._labelStandoff;
570590
var labelShift = ax._labelShift;
@@ -576,7 +596,8 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
576596
return offset4tx + offset4tick;
577597
};
578598

579-
ax._labelanchor = function(angle, d) {
599+
// TODO maybe switch angle, d ordering ??
600+
var labelAnchorFn = function(angle, d) {
580601
var rad = t2g(d);
581602
return signSin(rad) === 0 ?
582603
(signCos(rad) > 0 ? 'start' : 'end') :
@@ -590,13 +611,57 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
590611
}
591612

592613
ax.setScale();
593-
doTicksSingle(gd, ax, true);
614+
615+
var vals = Axes.calcTicks(ax);
616+
// Use tickval filter for category axes instead of tweaking
617+
// the range w.r.t sector, so that sectors that cross 360 can
618+
// show all their ticks.
619+
var tickVals = ax.type === 'category' ?
620+
vals.filter(function(d) { return Lib.isAngleInsideSector(t2g(d), _this.sectorInRad); }) :
621+
vals;
622+
623+
if(ax.visible) {
624+
// TODO dry!!
625+
var sideOpposite = 'right';
626+
var tickSign = [-1, 1, ax.side === sideOpposite ? 1 : -1];
627+
if((ax.ticks !== 'inside') === false) {
628+
tickSign = tickSign.map(function(v) { return -v; });
629+
}
630+
631+
var pad = tickSign[2] * (ax.linewidth || 1) / 2;
632+
var len = tickSign[2] * ax.ticklen;
633+
634+
Axes.drawTicks(gd, ax, {
635+
vals: tickVals,
636+
layer: layers['angular-axis'],
637+
path: 'M' + pad + ',0h' + len,
638+
transFn: transFn
639+
});
640+
641+
Axes.drawGrid(gd, ax, {
642+
vals: tickVals,
643+
layer: layers['angular-grid'],
644+
path: gridPathFn,
645+
transFn: transFn
646+
});
647+
648+
Axes.drawLabels(gd, ax, {
649+
vals: tickVals,
650+
layer: layers['angular-axis'],
651+
shift: 0,
652+
transFn: transFn,
653+
labelXFn: labelXFn,
654+
labelYFn: labelYFn,
655+
labelAnchorFn: labelAnchorFn,
656+
skipTitle: true
657+
});
658+
}
594659

595660
// angle of polygon vertices in geometric radians (null means circles)
596661
// TODO what to do when ax.period > ax._categories ??
597662
var vangles;
598663
if(polarLayout.gridshape === 'linear') {
599-
vangles = ax._vals.map(t2g);
664+
vangles = vals.map(t2g);
600665

601666
// ax._vals should be always ordered, make them
602667
// always turn counterclockwise for convenience here
@@ -1044,29 +1109,25 @@ proto.updateRadialDrag = function(fullLayout, polarLayout, rngIndex) {
10441109
return;
10451110
}
10461111

1112+
var fullLayoutNow = gd._fullLayout;
1113+
var polarLayoutNow = fullLayoutNow[_this.id];
1114+
10471115
// update radial range -> update c2g -> update _m,_b
10481116
radialAxis.range[rngIndex] = rprime;
10491117
radialAxis._rl[rngIndex] = rprime;
1050-
radialAxis.setGeometry();
1051-
radialAxis.setScale();
1118+
_this.updateRadialAxis(fullLayoutNow, polarLayoutNow);
10521119

10531120
_this.xaxis.setRange();
10541121
_this.xaxis.setScale();
10551122
_this.yaxis.setRange();
10561123
_this.yaxis.setScale();
10571124

1058-
doTicksSingle(gd, radialAxis, true);
1059-
layers['radial-grid']
1060-
.attr('transform', strTranslate(cx, cy))
1061-
.selectAll('path').attr('transform', null);
1062-
10631125
var hasRegl = false;
10641126

10651127
for(var traceType in _this.traceHash) {
10661128
var moduleCalcData = _this.traceHash[traceType];
10671129
var moduleCalcDataVisible = Lib.filterVisible(moduleCalcData);
10681130
var _module = moduleCalcData[0][0].trace._module;
1069-
var polarLayoutNow = gd._fullLayout[_this.id];
10701131
_module.plot(gd, _this, moduleCalcDataVisible, polarLayoutNow);
10711132
if(Registry.traceIs(traceType, 'gl') && moduleCalcDataVisible.length) hasRegl = true;
10721133
}
@@ -1140,6 +1201,7 @@ proto.updateAngularDrag = function(fullLayout) {
11401201
function moveFn(dx, dy) {
11411202
var fullLayoutNow = _this.gd._fullLayout;
11421203
var polarLayoutNow = fullLayoutNow[_this.id];
1204+
11431205
var x1 = x0 + dx;
11441206
var y1 = y0 + dy;
11451207
var a1 = xy2a(x1, y1);
@@ -1158,7 +1220,6 @@ proto.updateAngularDrag = function(fullLayout) {
11581220

11591221
layers.bg.attr('transform', trans);
11601222
layers['radial-grid'].attr('transform', trans);
1161-
layers['angular-line'].select('path').attr('transform', trans);
11621223
layers['radial-axis'].attr('transform', trans2);
11631224
layers['radial-line'].select('line').attr('transform', trans2);
11641225
_this.updateRadialAxisTitle(fullLayoutNow, polarLayoutNow, rrot1);
@@ -1184,10 +1245,7 @@ proto.updateAngularDrag = function(fullLayout) {
11841245

11851246
// update rotation -> range -> _m,_b
11861247
angularAxis.rotation = Lib.modHalf(rot1, 360);
1187-
angularAxis.setGeometry();
1188-
angularAxis.setScale();
1189-
1190-
doTicksSingle(gd, angularAxis, true);
1248+
_this.updateAngularAxis(fullLayoutNow, polarLayoutNow);
11911249

11921250
if(_this._hasClipOnAxisFalse && !Lib.isFullCircle(_this.sectorInRad)) {
11931251
scatterTraces.call(Drawing.hideOutsideRangePoints, _this);

test/jasmine/tests/polar_test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ describe('Test relayout on polar subplots:', function() {
428428
.then(toggle(
429429
'polar.angularaxis.showgrid',
430430
[true, false], [8, 0],
431-
'.angular-grid > .angularaxis > path', assertCnt
431+
'.angular-grid > path', assertCnt
432432
))
433433
.then(toggle(
434434
'polar.angularaxis.showticklabels',
@@ -608,7 +608,7 @@ describe('Test relayout on polar subplots:', function() {
608608
}
609609

610610
assertLetterCount('.plotbg > path');
611-
assertLetterCount('.radial-grid > .x > path');
611+
assertLetterCount('.radial-grid > path');
612612
assertLetterCount('.angular-line > path');
613613
}
614614

@@ -1303,7 +1303,7 @@ describe('Test polar *gridshape linear* interactions', function() {
13031303
var dragCoverNode;
13041304
var p1;
13051305

1306-
var layersRotateFromZero = ['.plotbg > path', '.radial-grid', '.angular-line > path'];
1306+
var layersRotateFromZero = ['.plotbg > path', '.radial-grid'];
13071307
var layersRotateFromRadialAxis = ['.radial-axis', '.radial-line > line'];
13081308

13091309
function _assertTransformRotate(msg, query, rot) {

0 commit comments

Comments
 (0)