Skip to content

Commit

Permalink
add 'inner' radial dragbox when polar.hole>0
Browse files Browse the repository at this point in the history
- which relayouts `radialaxis.range[0]`
- does not try to relayout `radialaxis.angle`
  • Loading branch information
etpinard committed Sep 7, 2018
1 parent 1122907 commit d466146
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 24 deletions.
62 changes: 38 additions & 24 deletions src/plots/polar/polar.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ proto.plot = function(polarCalcData, fullLayout) {
_this.updateLayers(fullLayout, polarLayout);
_this.updateLayout(fullLayout, polarLayout);
Plots.generalUpdatePerTraceModule(_this.gd, _this, polarCalcData, polarLayout);
_this.updateFx(fullLayout);
_this.updateFx(fullLayout, polarLayout);
};

proto.updateLayers = function(fullLayout, polarLayout) {
Expand Down Expand Up @@ -606,10 +606,11 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
.call(Color.stroke, angularLayout.linecolor);
};

proto.updateFx = function(fullLayout) {
proto.updateFx = function(fullLayout, polarLayout) {
if(!this.gd._context.staticPlot) {
this.updateAngularDrag(fullLayout);
this.updateRadialDrag(fullLayout);
this.updateRadialDrag(fullLayout, polarLayout, 1);
if(this.innerRadius) this.updateRadialDrag(fullLayout, polarLayout, 0);

This comment has been minimized.

Copy link
@alexcjohnson

alexcjohnson Sep 10, 2018

Collaborator

Two questions about this:

  • if hole is very small, does this overlap the plot area or does the mainDrag take precedence?
  • if hole is removed entirely, does this element get removed? I guess if the mainDrag takes precedence it might not have any practical effect, but still doesn't seem ideal...

This comment has been minimized.

Copy link
@etpinard

etpinard Sep 10, 2018

Author Contributor

if hole is very small, does this overlap the plot area or does the mainDrag take precedence?

That mainDrag is plotted last so it shouldn't interfere. I'll double-check though.

if hole is removed entirely, does this element get removed? I guess if the mainDrag takes precedence it might not have any practical effect, but still doesn't seem ideal...

Currently, it does not. Good point, we should remove it whenever hole is removed.

This comment has been minimized.

Copy link
@etpinard

etpinard Sep 10, 2018

Author Contributor

I decided to "solve" the problem a different way in 092006a, where the 'inner' radial drag box is now drawn on all polar subplots. For default subplots (with no hole and making up a full circle) the inner radial drag box will never get called (as it is fully hidden below the main drag), but subplots like

peek 2018-09-10 15-13

can now update both radialaxis.range[0] and radialaxis.range[1] with drag handles.

This comment has been minimized.

Copy link
@alexcjohnson

alexcjohnson Sep 10, 2018

Collaborator

Ah, that's a great idea 🎉

this.updateMainDrag(fullLayout);
}
};
Expand Down Expand Up @@ -913,40 +914,47 @@ proto.updateMainDrag = function(fullLayout) {
dragElement.init(dragOpts);
};

proto.updateRadialDrag = function(fullLayout) {
proto.updateRadialDrag = function(fullLayout, polarLayout, rngIndex) {
var _this = this;
var gd = _this.gd;
var layers = _this.layers;
var radius = _this.radius;
var innerRadius = _this.innerRadius;
var cx = _this.cx;
var cy = _this.cy;
var radialAxis = _this.radialAxis;

if(!radialAxis.visible) return;

var angle0 = deg2rad(_this.radialAxisAngle);
var rl0 = radialAxis._rl[0];
var rl1 = radialAxis._rl[1];
var drl = rl1 - rl0;
var rl = radialAxis._rl;
var rl0 = rl[0];
var rl1 = rl[1];
var rbase = rl[rngIndex];
var m = 0.75 * (rl[1] - rl[0]) / (1 - polarLayout.hole) / radius;

var bl = constants.radialDragBoxSize;
var bl2 = bl / 2;
var radialDrag = dragBox.makeRectDragger(layers, 'radialdrag', 'crosshair', -bl2, -bl2, bl, bl);
var className = 'radialdrag' + (rngIndex ? '' : '-inner');
var radialDrag = dragBox.makeRectDragger(layers, className, 'crosshair', -bl2, -bl2, bl, bl);
var dragOpts = {element: radialDrag, gd: gd};
var tx = cx + (radius + bl2) * Math.cos(angle0);
var ty = cy - (radius + bl2) * Math.sin(angle0);

// TODO add 'inner' drag box when innerRadius > 0 !!

d3.select(radialDrag)
.attr('transform', strTranslate(tx, ty));
var tx, ty;
if(rngIndex) {
tx = cx + (radius + bl2) * Math.cos(angle0);
ty = cy - (radius + bl2) * Math.sin(angle0);
} else {
tx = cx + (innerRadius - bl2) * Math.cos(angle0);
ty = cy - (innerRadius - bl2) * Math.sin(angle0);
}
d3.select(radialDrag).attr('transform', strTranslate(tx, ty));

// move function (either rotate or re-range flavor)
var moveFn2;
// rotate angle on done
var angle1;
// re-range range[1] on done
var rng1;
// re-range range[1] (or range[0]) on done
var rprime;

function moveFn(dx, dy) {
if(moveFn2) {
Expand All @@ -967,12 +975,15 @@ proto.updateRadialDrag = function(fullLayout) {
function doneFn() {
if(angle1 !== null) {
Registry.call('relayout', gd, _this.id + '.radialaxis.angle', angle1);
} else if(rng1 !== null) {
Registry.call('relayout', gd, _this.id + '.radialaxis.range[1]', rng1);
} else if(rprime !== null) {
Registry.call('relayout', gd, _this.id + '.radialaxis.range[' + rngIndex + ']', rprime);
}
}

function rotateMove(dx, dy) {
// disable for inner drag boxes
if(rngIndex === 0) return;

var x1 = tx + dx;
var y1 = ty + dy;

Expand All @@ -992,14 +1003,17 @@ proto.updateRadialDrag = function(fullLayout) {
function rerangeMove(dx, dy) {
// project (dx, dy) unto unit radial axis vector
var dr = Lib.dot([dx, -dy], [Math.cos(angle0), Math.sin(angle0)]);
rng1 = rl1 - drl * dr / radius * 0.75;
rprime = rbase - m * dr;

// make sure new range[1] does not change the range[0] -> range[1] sign
if((drl > 0) !== (rng1 > rl0)) return;
// make sure rprime does not change the range[0] -> range[1] sign
if((m > 0) !== (rngIndex ? rprime > rl0 : rprime < rl1)) {
rprime = null;
return;
}

// update radial range -> update c2g -> update _m,_b
radialAxis.range[1] = rng1;
radialAxis._rl[1] = rng1;
radialAxis.range[rngIndex] = rprime;
radialAxis._rl[rngIndex] = rprime;
radialAxis.setGeometry();
radialAxis.setScale();

Expand Down Expand Up @@ -1027,7 +1041,7 @@ proto.updateRadialDrag = function(fullLayout) {
dragOpts.prepFn = function() {
moveFn2 = null;
angle1 = null;
rng1 = null;
rprime = null;

dragOpts.moveFn = moveFn;
dragOpts.doneFn = doneFn;
Expand Down
39 changes: 39 additions & 0 deletions test/jasmine/tests/polar_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,45 @@ describe('Test polar interactions:', function() {
.then(done);
});

it('should response to drag interactions on inner radial drag area', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json'));
fig.layout.polar.hole = 0.2;
// to avoid dragging on hover labels
fig.layout.hovermode = false;
// adjust margins so that middle of plot area is at 300x300
// with its middle at [200,200]
fig.layout.width = 400;
fig.layout.height = 400;
fig.layout.margin = {l: 50, t: 50, b: 50, r: 50};

var dragPos0 = [200, 200];

// use 'special' drag method - as we need two mousemove events
// to activate the radial drag mode
function _drag(p0, dp) {
var node = d3.select('.polar > .draglayer > .radialdrag-inner').node();
return drag(node, dp[0], dp[1], null, p0[0], p0[1], 2);
}

function _assert(rng, msg) {
expect(gd._fullLayout.polar.radialaxis.range)
.toBeCloseToArray(rng, 1, msg + ' - range');
}

_plot(fig)
.then(function() { return _drag(dragPos0, [-50, 0]); })
.then(function() {
_assert([3.55, 11.36], 'move inward');
})
.then(function() { return Plotly.relayout(gd, 'polar.radialaxis.autorange', true); })
.then(function() { return _drag(dragPos0, [50, 0]); })
.then(function() {
_assert([-3.55, 11.36], 'move outward');
})
.catch(failTest)
.then(done);
});

it('should response to drag interactions on angular drag area', function(done) {
var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json'));

Expand Down

0 comments on commit d466146

Please sign in to comment.