Skip to content

Constrain axes by domain #1767

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

Merged
merged 23 commits into from
Jun 9, 2017
Merged
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d0af01b
axis.constrain and axis.constraintoward
alexcjohnson Jun 1, 2017
cc705c3
robustify plot_test
alexcjohnson Jun 1, 2017
b3a3bb7
simplify plot_test
alexcjohnson Jun 1, 2017
27fc2a9
robustify plot_api_test
alexcjohnson Jun 1, 2017
d937732
simplify hover_label_test
alexcjohnson Jun 2, 2017
1570274
no really, initInteractions! fix #1044... again
alexcjohnson Jun 2, 2017
72896ec
robustify drawing_test
alexcjohnson Jun 2, 2017
ee3211a
robustify gl_plot_interact_test
alexcjohnson Jun 2, 2017
f12ff73
streamline gl2d_click_test
alexcjohnson Jun 2, 2017
5f5214e
fix bug with unhover in gl2d
alexcjohnson Jun 2, 2017
9df6903
robustify mapbox_test
alexcjohnson Jun 2, 2017
d0b15c8
tests of axis domain constraints
alexcjohnson Jun 2, 2017
d67829e
create constraints.clean to get it out of the main Plotly.plot code
alexcjohnson Jun 8, 2017
b1c6b24
more robust way to generate relayout args during axis drag
alexcjohnson Jun 8, 2017
59a092a
update constraints.js to handle some more editing edge cases
alexcjohnson Jun 8, 2017
51fc2c6
put range 0,1 parts separately into GUI relayout calls
alexcjohnson Jun 8, 2017
3931973
rm unused `attrs` variable
alexcjohnson Jun 8, 2017
1271382
axes.expand: ensure domain constraint before adjusting extrappad
alexcjohnson Jun 8, 2017
6d3a48a
add special validateFunction for enumerated valType
etpinard Jun 8, 2017
3b0d829
add special handler in Plotly.validate for enumerated w/ dynamic values
etpinard Jun 8, 2017
4002fc1
Merge pull request #1769 from plotly/validate-dynamic-enumerated
etpinard Jun 8, 2017
fa78376
:hocho: debug cruft
alexcjohnson Jun 9, 2017
67ea3a8
test axis constraints (range & domain) with log and category axes
alexcjohnson Jun 9, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 30 additions & 27 deletions src/plots/cartesian/dragbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
zb,
corners;

// collected changes to be made to the plot by relayout at the end
var updates = {};

function zoomPrep(e, startX, startY) {
var dragBBox = dragger.getBoundingClientRect();
x0 = startX - dragBBox.left;
Expand Down Expand Up @@ -282,8 +285,8 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
}

// TODO: edit linked axes in zoomAxRanges and in dragTail
if(zoomMode === 'xy' || zoomMode === 'x') zoomAxRanges(xa, box.l / pw, box.r / pw, xaLinked);
if(zoomMode === 'xy' || zoomMode === 'y') zoomAxRanges(ya, (ph - box.b) / ph, (ph - box.t) / ph, yaLinked);
if(zoomMode === 'xy' || zoomMode === 'x') zoomAxRanges(xa, box.l / pw, box.r / pw, updates, xaLinked);
if(zoomMode === 'xy' || zoomMode === 'y') zoomAxRanges(ya, (ph - box.b) / ph, (ph - box.t) / ph, updates, yaLinked);

removeZoombox(gd);
dragTail(zoomMode);
Expand Down Expand Up @@ -335,11 +338,11 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
}

// scroll zoom, on all draggers except corners
var scrollViewBox = [0, 0, pw, ph],
// wait a little after scrolling before redrawing
redrawTimer = null,
REDRAWDELAY = constants.REDRAWDELAY,
mainplot = plotinfo.mainplot ?
var scrollViewBox = [0, 0, pw, ph];
// wait a little after scrolling before redrawing
var redrawTimer = null;
var REDRAWDELAY = constants.REDRAWDELAY;
var mainplot = plotinfo.mainplot ?
fullLayout._plots[plotinfo.mainplot] : plotinfo;

function zoomWheel(e) {
Expand Down Expand Up @@ -524,6 +527,8 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
ticksAndAnnotations(yActive, xActive);
}

// Draw ticks and annotations (and other components) when ranges change.
// Also records the ranges that have changed for use by update at the end.
function ticksAndAnnotations(ns, ew) {
var activeAxIds = [],
i;
Expand All @@ -543,8 +548,12 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
pushActiveAxIds(yaLinked);
}

updates = {};
for(i = 0; i < activeAxIds.length; i++) {
doTicks(gd, activeAxIds[i], true);
var axId = activeAxIds[i];
doTicks(gd, axId, true);
var ax = getFromId(gd, axId);
updates[ax._name + '.range'] = ax.range.slice();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@etpinard this is significantly simpler than what I was doing before (calculating the range edits again in dragTail) and more robust since it's explicitly WYSIWYG - whenever you update tick labels you record that and use it as the basis of the update object to be sent to relayout.

Zoombox has a counterpart here

Oh, I'm noticing now that previously we sent each end of the range as a separate edit in the update object (ie 'xaxis.range[0]': 1, 'xaxis.range[1]': 5 rather than 'xaxis.range': [1, 5]) - this way is more compact but I bet it'll break some applications that listen to relayout events to respond to zooms and inspect the event data to find the range changes, so I think I should change it back to the previous form.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

note that one effect of doing it this way is when you drag axis ends or make a zoombox (any of the GUI interactions) while using constrain: 'domain', the shape and constrained domains of the subplot will never change because we always make proportional updates to all linked ranges. This is NOT the case for generic relayout calls: if you relayout a single range, it can reshape the subplot, potentially changing which axis is constrained, but always keeping the domains as large as possible within the constraint.

Copy link
Contributor

@etpinard etpinard Jun 8, 2017

Choose a reason for hiding this comment

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

so I think I should change it back to the previous form.

I agree. Listening to plotly_relayout and inspecting the event data for xaxis.range[0] or xaxis.range[1] must be pretty common in userland.

We should try to clean that up in v2 or maybe we could add a plotly_rerange?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

-> back to independent attributes in 51fc2c6

}

function redrawObjs(objArray, method, shortCircuit) {
Expand Down Expand Up @@ -641,29 +650,17 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
function dragTail(zoommode) {
if(zoommode === undefined) zoommode = (ew ? 'x' : '') + (ns ? 'y' : '');

var attrs = {};
// revert to the previous axis settings, then apply the new ones
// through relayout - this lets relayout manage undo/redo
var axesToModify;
if(zoommode === 'xy') axesToModify = xa.concat(ya);
else if(zoommode === 'x') axesToModify = xa;
else if(zoommode === 'y') axesToModify = ya;

for(var i = 0; i < axesToModify.length; i++) {
var axi = axesToModify[i];
if(axi._r[0] !== axi.range[0]) attrs[axi._name + '.range[0]'] = axi.range[0];
if(axi._r[1] !== axi.range[1]) attrs[axi._name + '.range[1]'] = axi.range[1];

axi.range = axi._input.range = axi._r.slice();
}

// put the subplot viewboxes back to default (Because we're going to)
// be repositioning the data in the relayout. But DON'T call
// ticksAndAnnotations again - it's unnecessary and would overwrite `updates`
updateSubplots([0, 0, pw, ph]);
Plotly.relayout(gd, attrs);
Plotly.relayout(gd, updates);
}

// updateSubplots - find all plot viewboxes that should be
// affected by this drag, and update them. look for all plots
// sharing an affected axis (including the one being dragged)
// returns all the new axis ranges as an update object
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oops, this was an aborted attempt, I'm not using thisattrs so will remove it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

-> removed this attrs in 3931973

function updateSubplots(viewBox) {
var plotinfos = fullLayout._plots;
var subplots = Object.keys(plotinfos);
Expand All @@ -674,6 +671,8 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {

var i, xScaleFactor2, yScaleFactor2, clipDx, clipDy;

var attrs = {};

// Find the appropriate scaling for this axis, if it's linked to the
// dragged axes by constraints. 0 is special, it means this axis shouldn't
// ever be scaled (will be converted to 1 if the other axis is scaled)
Expand All @@ -693,6 +692,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
if(scaleFactor) {
ax.range = ax._r.slice();
scaleZoom(ax, scaleFactor);
attrs[ax._name + '.range'] = ax.range.slice();
return getShift(ax, scaleFactor);
}
return 0;
Expand Down Expand Up @@ -758,6 +758,8 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
.selectAll('.points').selectAll('.textpoint')
.call(Drawing.setTextPointsScale, xScaleFactor2, yScaleFactor2);
}

return attrs;
}

return dragger;
Expand Down Expand Up @@ -806,7 +808,7 @@ function getEndText(ax, end) {
}
}

function zoomAxRanges(axList, r0Fraction, r1Fraction, linkedAxes) {
function zoomAxRanges(axList, r0Fraction, r1Fraction, updates, linkedAxes) {
var i,
axi,
axRangeLinear0,
Expand All @@ -822,13 +824,14 @@ function zoomAxRanges(axList, r0Fraction, r1Fraction, linkedAxes) {
axi.l2r(axRangeLinear0 + axRangeLinearSpan * r0Fraction),
axi.l2r(axRangeLinear0 + axRangeLinearSpan * r1Fraction)
];
updates[axi._name + '.range'] = axi.range.slice();
}

// zoom linked axes about their centers
if(linkedAxes && linkedAxes.length) {
var linkedR0Fraction = (r0Fraction + (1 - r1Fraction)) / 2;

zoomAxRanges(linkedAxes, linkedR0Fraction, 1 - linkedR0Fraction);
zoomAxRanges(linkedAxes, linkedR0Fraction, 1 - linkedR0Fraction, updates);
}
}

Expand Down