-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Constrain axes by domain #1767
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 cc705c3
robustify plot_test
alexcjohnson b3a3bb7
simplify plot_test
alexcjohnson 27fc2a9
robustify plot_api_test
alexcjohnson d937732
simplify hover_label_test
alexcjohnson 1570274
no really, initInteractions! fix #1044... again
alexcjohnson 72896ec
robustify drawing_test
alexcjohnson ee3211a
robustify gl_plot_interact_test
alexcjohnson f12ff73
streamline gl2d_click_test
alexcjohnson 5f5214e
fix bug with unhover in gl2d
alexcjohnson 9df6903
robustify mapbox_test
alexcjohnson d0b15c8
tests of axis domain constraints
alexcjohnson d67829e
create constraints.clean to get it out of the main Plotly.plot code
alexcjohnson b1c6b24
more robust way to generate relayout args during axis drag
alexcjohnson 59a092a
update constraints.js to handle some more editing edge cases
alexcjohnson 51fc2c6
put range 0,1 parts separately into GUI relayout calls
alexcjohnson 3931973
rm unused `attrs` variable
alexcjohnson 1271382
axes.expand: ensure domain constraint before adjusting extrappad
alexcjohnson 6d3a48a
add special validateFunction for enumerated valType
etpinard 3b0d829
add special handler in Plotly.validate for enumerated w/ dynamic values
etpinard 4002fc1
Merge pull request #1769 from plotly/validate-dynamic-enumerated
etpinard fa78376
:hocho: debug cruft
alexcjohnson 67ea3a8
test axis constraints (range & domain) with log and category axes
alexcjohnson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
|
@@ -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); | ||
|
@@ -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) { | ||
|
@@ -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; | ||
|
@@ -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(); | ||
} | ||
|
||
function redrawObjs(objArray, method, shortCircuit) { | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oops, this was an aborted attempt, I'm not using this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. -> removed this |
||
function updateSubplots(viewBox) { | ||
var plotinfos = fullLayout._plots; | ||
var subplots = Object.keys(plotinfos); | ||
|
@@ -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) | ||
|
@@ -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; | ||
|
@@ -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; | ||
|
@@ -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, | ||
|
@@ -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); | ||
} | ||
} | ||
|
||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
@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.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.
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 genericrelayout
calls: if yourelayout
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.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.
I agree. Listening to
plotly_relayout
and inspecting the event data forxaxis.range[0]
orxaxis.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
?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.
-> back to independent attributes in 51fc2c6