@@ -42,10 +42,16 @@ function overlappingDomain(xDomain, yDomain, domains) {
4242}
4343
4444exports . lsInner = function ( gd ) {
45- var fullLayout = gd . _fullLayout ,
46- gs = fullLayout . _size ,
47- axList = Plotly . Axes . list ( gd ) ,
48- i ;
45+ var fullLayout = gd . _fullLayout ;
46+ var gs = fullLayout . _size ;
47+ var pad = gs . p ;
48+ var axList = Plotly . Axes . list ( gd ) ;
49+
50+ // _has('cartesian') means SVG specifically, not GL2D - but GL2D
51+ // can still get here because it makes some of the SVG structure
52+ // for shared features like selections.
53+ var hasSVGCartesian = fullLayout . _has ( 'cartesian' ) ;
54+ var i ;
4955
5056 // clear axis line positions, to be set in the subplot loop below
5157 for ( i = 0 ; i < axList . length ; i ++ ) axList [ i ] . _linepositions = { } ;
@@ -80,11 +86,9 @@ exports.lsInner = function(gd) {
8086 return ;
8187 }
8288
83- var xa = Plotly . Axes . getFromId ( gd , subplot , 'x' ) ,
84- ya = Plotly . Axes . getFromId ( gd , subplot , 'y' ) ,
85- xDomain = xa . domain ,
86- yDomain = ya . domain ,
87- plotgroupBgData = [ ] ;
89+ var xDomain = plotinfo . xaxis . domain ;
90+ var yDomain = plotinfo . yaxis . domain ;
91+ var plotgroupBgData = [ ] ;
8892
8993 if ( overlappingDomain ( xDomain , yDomain , lowerDomains ) ) {
9094 plotgroupBgData = [ 0 ] ;
@@ -125,22 +129,21 @@ exports.lsInner = function(gd) {
125129 fullLayout . _plots [ subplot ] . bg = d3 . select ( this ) ;
126130 } ) ;
127131
128- var freefinished = [ ] ;
132+ var freeFinished = { } ;
129133 subplotSelection . each ( function ( subplot ) {
130134 var plotinfo = fullLayout . _plots [ subplot ] ;
131-
132- var xa = Plotly . Axes . getFromId ( gd , subplot , 'x' ) ,
133- ya = Plotly . Axes . getFromId ( gd , subplot , 'y' ) ;
135+ var xa = plotinfo . xaxis ;
136+ var ya = plotinfo . yaxis ;
134137
135138 // reset scale in case the margins have changed
136139 xa . setScale ( ) ;
137140 ya . setScale ( ) ;
138141
139- if ( plotinfo . bg && fullLayout . _has ( 'cartesian' ) ) {
142+ if ( plotinfo . bg && hasSVGCartesian ) {
140143 plotinfo . bg
141144 . call ( Drawing . setRect ,
142- xa . _offset - gs . p , ya . _offset - gs . p ,
143- xa . _length + 2 * gs . p , ya . _length + 2 * gs . p )
145+ xa . _offset - pad , ya . _offset - pad ,
146+ xa . _length + 2 * pad , ya . _length + 2 * pad )
144147 . call ( Color . fill , fullLayout . plot_bgcolor )
145148 . style ( 'stroke-width' , 0 ) ;
146149 }
@@ -165,7 +168,7 @@ exports.lsInner = function(gd) {
165168 'height' : ya . _length
166169 } ) ;
167170
168- plotinfo . plot . call ( Drawing . setTranslate , xa . _offset , ya . _offset ) ;
171+ Drawing . setTranslate ( plotinfo . plot , xa . _offset , ya . _offset ) ;
169172
170173 var plotClipId ;
171174 var layerClipId ;
@@ -191,126 +194,153 @@ exports.lsInner = function(gd) {
191194 // to DRY up Drawing.setClipUrl calls downstream
192195 plotinfo . layerClipId = layerClipId ;
193196
194- var xlw = Drawing . crispRound ( gd , xa . linewidth , 1 ) ,
195- ylw = Drawing . crispRound ( gd , ya . linewidth , 1 ) ,
196- xp = gs . p + ylw ,
197- xpathPrefix = 'M' + ( - xp ) + ',' ,
198- xpathSuffix = 'h' + ( xa . _length + 2 * xp ) ,
199- showfreex = xa . anchor === 'free' &&
200- freefinished . indexOf ( xa . _id ) === - 1 ,
201- freeposx = gs . h * ( 1 - ( xa . position || 0 ) ) + ( ( xlw / 2 ) % 1 ) ,
202- showbottom =
203- ( xa . anchor === ya . _id && ( xa . mirror || xa . side !== 'top' ) ) ||
204- xa . mirror === 'all' || xa . mirror === 'allticks' ||
205- ( xa . mirrors && xa . mirrors [ ya . _id + 'bottom' ] ) ,
206- bottompos = ya . _length + gs . p + xlw / 2 ,
207- showtop =
208- ( xa . anchor === ya . _id && ( xa . mirror || xa . side === 'top' ) ) ||
209- xa . mirror === 'all' || xa . mirror === 'allticks' ||
210- ( xa . mirrors && xa . mirrors [ ya . _id + 'top' ] ) ,
211- toppos = - gs . p - xlw / 2 ,
212-
213- // shorten y axis lines so they don't overlap x axis lines
214- yp = gs . p ,
215- // except where there's no x line
216- // TODO: this gets more complicated with multiple x and y axes
217- ypbottom = showbottom ? 0 : xlw ,
218- yptop = showtop ? 0 : xlw ,
219- ypathSuffix = ',' + ( - yp - yptop ) +
220- 'v' + ( ya . _length + 2 * yp + yptop + ypbottom ) ,
221- showfreey = ya . anchor === 'free' &&
222- freefinished . indexOf ( ya . _id ) === - 1 ,
223- freeposy = gs . w * ( ya . position || 0 ) + ( ( ylw / 2 ) % 1 ) ,
224- showleft =
225- ( ya . anchor === xa . _id && ( ya . mirror || ya . side !== 'right' ) ) ||
226- ya . mirror === 'all' || ya . mirror === 'allticks' ||
227- ( ya . mirrors && ya . mirrors [ xa . _id + 'left' ] ) ,
228- leftpos = - gs . p - ylw / 2 ,
229- showright =
230- ( ya . anchor === xa . _id && ( ya . mirror || ya . side === 'right' ) ) ||
231- ya . mirror === 'all' || ya . mirror === 'allticks' ||
232- ( ya . mirrors && ya . mirrors [ xa . _id + 'right' ] ) ,
233- rightpos = xa . _length + gs . p + ylw / 2 ;
197+ var xIsFree = ! xa . _anchorAxis ;
198+ var showFreeX = xIsFree && ! freeFinished [ xa . _id ] ;
199+ var showBottom = shouldShowLine ( xa , ya , 'bottom' ) ;
200+ var showTop = shouldShowLine ( xa , ya , 'top' ) ;
201+
202+ var yIsFree = ! ya . _anchorAxis ;
203+ var showFreeY = yIsFree && ! freeFinished [ ya . _id ] ;
204+ var showLeft = shouldShowLine ( ya , xa , 'left' ) ;
205+ var showRight = shouldShowLine ( ya , xa , 'right' ) ;
206+
207+ var xlw = Drawing . crispRound ( gd , xa . linewidth , 1 ) ;
208+ var ylw = Drawing . crispRound ( gd , ya . linewidth , 1 ) ;
209+
210+ /*
211+ * x lines get longer where they meet y lines, to make a crisp corner.
212+ * The x lines get the padding (margin.pad) plus the y line width to
213+ * fill up the corner nicely. Free x lines are excluded - they always
214+ * span exactly the data area of the plot
215+ *
216+ * | XXXXX
217+ * | XXXXX
218+ * |
219+ * +------
220+ * x1
221+ * -----
222+ * x2
223+ */
224+ var leftYLineWidth = findCounterAxisLineWidth ( gd , xa , ylw , showLeft , 'left' , axList ) ;
225+ var xLinesXLeft = ( ! xIsFree && leftYLineWidth ) ?
226+ ( - pad - leftYLineWidth ) : 0 ;
227+ var rightYLineWidth = findCounterAxisLineWidth ( gd , xa , ylw , showRight , 'right' , axList ) ;
228+ var xLinesXRight = xa . _length + ( ( ! xIsFree && rightYLineWidth ) ?
229+ ( pad + rightYLineWidth ) : 0 ) ;
230+ var xLinesYFree = gs . h * ( 1 - ( xa . position || 0 ) ) + ( ( xlw / 2 ) % 1 ) ;
231+ var xLinesYBottom = ya . _length + pad + xlw / 2 ;
232+ var xLinesYTop = - pad - xlw / 2 ;
233+
234+ /*
235+ * y lines that meet x axes get longer only by margin.pad, because
236+ * the x axes fill in the corner space. Free y axes, like free x axes,
237+ * always span exactly the data area of the plot
238+ *
239+ * | | XXXX
240+ * y2| y1| XXXX
241+ * | | XXXX
242+ * |
243+ * +-----
244+ */
245+ var connectYBottom = ! yIsFree && findCounterAxisLineWidth (
246+ gd , ya , xlw , showBottom , 'bottom' , axList ) ;
247+ var yLinesYBottom = ya . _length + ( connectYBottom ? pad : 0 ) ;
248+ var connectYTop = ! yIsFree && findCounterAxisLineWidth (
249+ gd , ya , xlw , showTop , 'top' , axList ) ;
250+ var yLinesYTop = connectYTop ? - pad : 0 ;
251+ var yLinesXFree = gs . w * ( ya . position || 0 ) + ( ( ylw / 2 ) % 1 ) ;
252+ var yLinesXLeft = - pad - ylw / 2 ;
253+ var yLinesXRight = xa . _length + pad + ylw / 2 ;
254+
255+ function xLinePath ( y , showThis ) {
256+ if ( ! showThis ) return '' ;
257+ return 'M' + xLinesXLeft + ',' + y + 'H' + xLinesXRight ;
258+ }
259+
260+ function yLinePath ( x , showThis ) {
261+ if ( ! showThis ) return '' ;
262+ return 'M' + x + ',' + yLinesYTop + 'V' + yLinesYBottom ;
263+ }
234264
235265 // save axis line positions for ticks, draggers, etc to reference
236266 // each subplot gets an entry:
237267 // [left or bottom, right or top, free, main]
238268 // main is the position at which to draw labels and draggers, if any
239269 xa . _linepositions [ subplot ] = [
240- showbottom ? bottompos : undefined ,
241- showtop ? toppos : undefined ,
242- showfreex ? freeposx : undefined
270+ showBottom ? xLinesYBottom : undefined ,
271+ showTop ? xLinesYTop : undefined ,
272+ showFreeX ? xLinesYFree : undefined
243273 ] ;
244- if ( xa . anchor === ya . _id ) {
274+ if ( xa . _anchorAxis === ya ) {
245275 xa . _linepositions [ subplot ] [ 3 ] = xa . side === 'top' ?
246- toppos : bottompos ;
276+ xLinesYTop : xLinesYBottom ;
247277 }
248- else if ( showfreex ) {
249- xa . _linepositions [ subplot ] [ 3 ] = freeposx ;
278+ else if ( showFreeX ) {
279+ xa . _linepositions [ subplot ] [ 3 ] = xLinesYFree ;
250280 }
251281
252282 ya . _linepositions [ subplot ] = [
253- showleft ? leftpos : undefined ,
254- showright ? rightpos : undefined ,
255- showfreey ? freeposy : undefined
283+ showLeft ? yLinesXLeft : undefined ,
284+ showRight ? yLinesXRight : undefined ,
285+ showFreeY ? yLinesXFree : undefined
256286 ] ;
257- if ( ya . anchor === xa . _id ) {
287+ if ( ya . _anchorAxis === xa ) {
258288 ya . _linepositions [ subplot ] [ 3 ] = ya . side === 'right' ?
259- rightpos : leftpos ;
289+ yLinesXRight : yLinesXLeft ;
260290 }
261- else if ( showfreey ) {
262- ya . _linepositions [ subplot ] [ 3 ] = freeposy ;
291+ else if ( showFreeY ) {
292+ ya . _linepositions [ subplot ] [ 3 ] = yLinesXFree ;
263293 }
264294
265295 // translate all the extra stuff to have the
266296 // same origin as the plot area or axes
267- var origin = 'translate(' + xa . _offset + ',' + ya . _offset + ')' ,
268- originx = origin ,
269- originy = origin ;
270- if ( showfreex ) {
271- originx = 'translate(' + xa . _offset + ',' + gs . t + ')' ;
272- toppos += ya . _offset - gs . t ;
273- bottompos += ya . _offset - gs . t ;
297+ var origin = 'translate(' + xa . _offset + ',' + ya . _offset + ')' ;
298+ var originX = origin ;
299+ var originY = origin ;
300+ if ( showFreeX ) {
301+ originX = 'translate(' + xa . _offset + ',' + gs . t + ')' ;
302+ xLinesYTop += ya . _offset - gs . t ;
303+ xLinesYBottom += ya . _offset - gs . t ;
274304 }
275- if ( showfreey ) {
276- originy = 'translate(' + gs . l + ',' + ya . _offset + ')' ;
277- leftpos += xa . _offset - gs . l ;
278- rightpos += xa . _offset - gs . l ;
305+ if ( showFreeY ) {
306+ originY = 'translate(' + gs . l + ',' + ya . _offset + ')' ;
307+ yLinesXLeft += xa . _offset - gs . l ;
308+ yLinesXRight += xa . _offset - gs . l ;
279309 }
280310
281- if ( fullLayout . _has ( 'cartesian' ) ) {
311+ if ( hasSVGCartesian ) {
282312 plotinfo . xlines
283- . attr ( 'transform' , originx )
313+ . attr ( 'transform' , originX )
284314 . attr ( 'd' , (
285- ( showbottom ? ( xpathPrefix + bottompos + xpathSuffix ) : '' ) +
286- ( showtop ? ( xpathPrefix + toppos + xpathSuffix ) : '' ) +
287- ( showfreex ? ( xpathPrefix + freeposx + xpathSuffix ) : '' ) ) ||
315+ xLinePath ( xLinesYBottom , showBottom ) +
316+ xLinePath ( xLinesYTop , showTop ) +
317+ xLinePath ( xLinesYFree , showFreeX ) ) ||
288318 // so it doesn't barf with no lines shown
289319 'M0,0' )
290320 . style ( 'stroke-width' , xlw + 'px' )
291321 . call ( Color . stroke , xa . showline ?
292322 xa . linecolor : 'rgba(0,0,0,0)' ) ;
293323 plotinfo . ylines
294- . attr ( 'transform' , originy )
324+ . attr ( 'transform' , originY )
295325 . attr ( 'd' , (
296- ( showleft ? ( 'M' + leftpos + ypathSuffix ) : '' ) +
297- ( showright ? ( 'M' + rightpos + ypathSuffix ) : '' ) +
298- ( showfreey ? ( 'M' + freeposy + ypathSuffix ) : '' ) ) ||
326+ yLinePath ( yLinesXLeft , showLeft ) +
327+ yLinePath ( yLinesXRight , showRight ) +
328+ yLinePath ( yLinesXFree , showFreeY ) ) ||
299329 'M0,0' )
300- . attr ( 'stroke-width' , ylw + 'px' )
330+ . style ( 'stroke-width' , ylw + 'px' )
301331 . call ( Color . stroke , ya . showline ?
302332 ya . linecolor : 'rgba(0,0,0,0)' ) ;
303333 }
304334
305- plotinfo . xaxislayer . attr ( 'transform' , originx ) ;
306- plotinfo . yaxislayer . attr ( 'transform' , originy ) ;
335+ plotinfo . xaxislayer . attr ( 'transform' , originX ) ;
336+ plotinfo . yaxislayer . attr ( 'transform' , originY ) ;
307337 plotinfo . gridlayer . attr ( 'transform' , origin ) ;
308338 plotinfo . zerolinelayer . attr ( 'transform' , origin ) ;
309339 plotinfo . draglayer . attr ( 'transform' , origin ) ;
310340
311341 // mark free axes as displayed, so we don't draw them again
312- if ( showfreex ) { freefinished . push ( xa . _id ) ; }
313- if ( showfreey ) { freefinished . push ( ya . _id ) ; }
342+ if ( showFreeX ) freeFinished [ xa . _id ] = 1 ;
343+ if ( showFreeY ) freeFinished [ ya . _id ] = 1 ;
314344 } ) ;
315345
316346 Plotly . Axes . makeClipPaths ( gd ) ;
@@ -320,6 +350,65 @@ exports.lsInner = function(gd) {
320350 return gd . _promises . length && Promise . all ( gd . _promises ) ;
321351} ;
322352
353+ function shouldShowLine ( ax , counterAx , side ) {
354+ return ( ax . _anchorAxis === counterAx && ( ax . mirror || ax . side === side ) ) ||
355+ ax . mirror === 'all' || ax . mirror === 'allticks' ||
356+ ( ax . mirrors && ax . mirrors [ counterAx . _id + side ] ) ;
357+ }
358+
359+ function findCounterAxes ( gd , ax , axList ) {
360+ var counterAxes = [ ] ;
361+ var anchorAx = ax . _anchorAxis ;
362+ if ( anchorAx ) {
363+ var counterMain = anchorAx . _mainAxis ;
364+ if ( counterAxes . indexOf ( counterMain ) === - 1 ) {
365+ counterAxes . push ( counterMain ) ;
366+ for ( var i = 0 ; i < axList . length ; i ++ ) {
367+ if ( axList [ i ] . overlaying === counterMain . _id &&
368+ counterAxes . indexOf ( axList [ i ] ) === - 1
369+ ) {
370+ counterAxes . push ( axList [ i ] ) ;
371+ }
372+ }
373+ }
374+ }
375+ return counterAxes ;
376+ }
377+
378+ function findLineWidth ( gd , axes , side ) {
379+ for ( var i = 0 ; i < axes . length ; i ++ ) {
380+ var ax = axes [ i ] ;
381+ var anchorAx = ax . _anchorAxis ;
382+ if ( anchorAx && shouldShowLine ( ax , anchorAx , side ) ) {
383+ return Drawing . crispRound ( gd , ax . linewidth ) ;
384+ }
385+ }
386+ }
387+
388+ function findCounterAxisLineWidth ( gd , ax , subplotCounterLineWidth ,
389+ subplotCounterIsShown , side , axList ) {
390+ if ( subplotCounterIsShown ) return subplotCounterLineWidth ;
391+
392+ var i ;
393+
394+ // find all counteraxes for this one, then of these, find the
395+ // first one that has a visible line on this side
396+ var mainAxis = ax . _mainAxis ;
397+ var counterAxes = findCounterAxes ( gd , mainAxis , axList ) ;
398+
399+ var lineWidth = findLineWidth ( gd , counterAxes , side ) ;
400+ if ( lineWidth ) return lineWidth ;
401+
402+ for ( i = 0 ; i < axList . length ; i ++ ) {
403+ if ( axList [ i ] . overlaying === mainAxis . _id ) {
404+ counterAxes = findCounterAxes ( gd , axList [ i ] , axList ) ;
405+ lineWidth = findLineWidth ( gd , counterAxes , side ) ;
406+ if ( lineWidth ) return lineWidth ;
407+ }
408+ }
409+ return 0 ;
410+ }
411+
323412exports . drawMainTitle = function ( gd ) {
324413 var fullLayout = gd . _fullLayout ;
325414
0 commit comments