@@ -37,13 +37,16 @@ exports.enforce = function enforceAxisConstraints(gd) {
3737 var matchScale = Infinity ;
3838 var normScales = { } ;
3939 var axes = { } ;
40+ var hasAnyDomainConstraint = false ;
4041
4142 // find the (normalized) scale of each axis in the group
4243 for ( j = 0 ; j < axisIDs . length ; j ++ ) {
4344 axisID = axisIDs [ j ] ;
4445 axes [ axisID ] = ax = fullLayout [ id2name ( axisID ) ] ;
4546
46- if ( ! ax . _inputDomain ) ax . _inputDomain = ax . domain . slice ( ) ;
47+ if ( ax . _inputDomain ) ax . domain = ax . _inputDomain . slice ( ) ;
48+ else ax . _inputDomain = ax . domain . slice ( ) ;
49+
4750 if ( ! ax . _inputRange ) ax . _inputRange = ax . range . slice ( ) ;
4851
4952 // set axis scale here so we can use _m rather than
@@ -53,18 +56,19 @@ exports.enforce = function enforceAxisConstraints(gd) {
5356 // abs: inverted scales still satisfy the constraint
5457 normScales [ axisID ] = normScale = Math . abs ( ax . _m ) / group [ axisID ] ;
5558 minScale = Math . min ( minScale , normScale ) ;
56- if ( ax . _constraintShrinkable ) {
57- // this has served its purpose, so remove it
58- delete ax . _constraintShrinkable ;
59- }
60- else {
59+ if ( ax . constrain === 'domain' || ! ax . _constraintShrinkable ) {
6160 matchScale = Math . min ( matchScale , normScale ) ;
6261 }
62+
63+ // this has served its purpose, so remove it
64+ delete ax . _constraintShrinkable ;
6365 maxScale = Math . max ( maxScale , normScale ) ;
66+
67+ if ( ax . constrain === 'domain' ) hasAnyDomainConstraint = true ;
6468 }
6569
6670 // Do we have a constraint mismatch? Give a small buffer for rounding errors
67- if ( minScale > ALMOST_EQUAL * maxScale ) continue ;
71+ if ( minScale > ALMOST_EQUAL * maxScale && ! hasAnyDomainConstraint ) continue ;
6872
6973 // now increase any ranges we need to until all normalized scales are equal
7074 for ( j = 0 ; j < axisIDs . length ; j ++ ) {
@@ -107,8 +111,7 @@ exports.enforce = function enforceAxisConstraints(gd) {
107111 factor *= rangeShrunk ;
108112 }
109113
110- // TODO
111- if ( ax . autorange ) {
114+ if ( ax . autorange && ax . _min . length && ax . _max . length ) {
112115 /*
113116 * range & factor may need to change because range was
114117 * calculated for the larger scaling, so some pixel
@@ -121,12 +124,16 @@ exports.enforce = function enforceAxisConstraints(gd) {
121124 * outerMin/Max are for - if the expansion was going to
122125 * go beyond the original domain, it must be impossible
123126 */
124- var rangeMin = Math . min ( ax . range [ 0 ] , ax . range [ 1 ] ) ;
125- var rangeMax = Math . max ( ax . range [ 0 ] , ax . range [ 1 ] ) ;
126- var rangeCenter = ( rangeMin + rangeMax ) / 2 ;
127- var halfRange = rangeMax - rangeCenter ;
128- var outerMin = rangeCenter - halfRange * factor ;
129- var outerMax = rangeCenter + halfRange * factor ;
127+ var rl0 = ax . r2l ( ax . range [ 0 ] ) ;
128+ var rl1 = ax . r2l ( ax . range [ 1 ] ) ;
129+ var rangeCenter = ( rl0 + rl1 ) / 2 ;
130+ var rangeMin = rangeCenter ;
131+ var rangeMax = rangeCenter ;
132+ var halfRange = Math . abs ( rl1 - rangeCenter ) ;
133+ // extra tiny bit for rounding errors, in case we actually
134+ // *are* expanding to the full domain
135+ var outerMin = rangeCenter - halfRange * factor * 1.0001 ;
136+ var outerMax = rangeCenter + halfRange * factor * 1.0001 ;
130137
131138 updateDomain ( ax , factor ) ;
132139 ax . setScale ( ) ;
@@ -135,34 +142,26 @@ exports.enforce = function enforceAxisConstraints(gd) {
135142 var k ;
136143
137144 for ( k = 0 ; k < ax . _min . length ; k ++ ) {
138- newVal = ax . _min [ i ] . val - ax . _min [ i ] . pad / m ;
145+ newVal = ax . _min [ k ] . val - ax . _min [ k ] . pad / m ;
139146 if ( newVal > outerMin && newVal < rangeMin ) {
140147 rangeMin = newVal ;
141148 }
142149 }
143150
144151 for ( k = 0 ; k < ax . _max . length ; k ++ ) {
145- newVal = ax . _max [ i ] . val + ax . _max [ i ] . pad / m ;
152+ newVal = ax . _max [ k ] . val + ax . _max [ k ] . pad / m ;
146153 if ( newVal < outerMax && newVal > rangeMax ) {
147154 rangeMax = newVal ;
148155 }
149156 }
150157
151- ax . range = ax . _input . range = ( ax . range [ 0 ] < ax . range [ 1 ] ) ?
152- [ rangeMin , rangeMax ] : [ rangeMax , rangeMin ] ;
153-
154- /*
155- * In principle this new range can be shifted vs. what
156- * you saw at the end of a zoom operation, like if you
157- * have a big bubble on one side and a small bubble on
158- * the other.
159- * To fix this we'd have to be doing this calculation
160- * continuously during the zoom, but it's enough of an
161- * edge case and a subtle enough effect that I'm going
162- * to ignore it for now.
163- */
164158 var domainExpand = ( rangeMax - rangeMin ) / ( 2 * halfRange ) ;
165159 factor /= domainExpand ;
160+
161+ rangeMin = ax . l2r ( rangeMin ) ;
162+ rangeMax = ax . l2r ( rangeMax ) ;
163+ ax . range = ax . _input . range = ( rl0 < rl1 ) ?
164+ [ rangeMin , rangeMax ] : [ rangeMax , rangeMin ] ;
166165 }
167166
168167 updateDomain ( ax , factor ) ;
0 commit comments