@@ -42,10 +42,16 @@ function overlappingDomain(xDomain, yDomain, domains) {
42
42
}
43
43
44
44
exports . 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 ;
49
55
50
56
// clear axis line positions, to be set in the subplot loop below
51
57
for ( i = 0 ; i < axList . length ; i ++ ) axList [ i ] . _linepositions = { } ;
@@ -80,11 +86,9 @@ exports.lsInner = function(gd) {
80
86
return ;
81
87
}
82
88
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 = [ ] ;
88
92
89
93
if ( overlappingDomain ( xDomain , yDomain , lowerDomains ) ) {
90
94
plotgroupBgData = [ 0 ] ;
@@ -125,22 +129,21 @@ exports.lsInner = function(gd) {
125
129
fullLayout . _plots [ subplot ] . bg = d3 . select ( this ) ;
126
130
} ) ;
127
131
128
- var freefinished = [ ] ;
132
+ var freeFinished = { } ;
129
133
subplotSelection . each ( function ( subplot ) {
130
134
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 ;
134
137
135
138
// reset scale in case the margins have changed
136
139
xa . setScale ( ) ;
137
140
ya . setScale ( ) ;
138
141
139
- if ( plotinfo . bg && fullLayout . _has ( 'cartesian' ) ) {
142
+ if ( plotinfo . bg && hasSVGCartesian ) {
140
143
plotinfo . bg
141
144
. 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 )
144
147
. call ( Color . fill , fullLayout . plot_bgcolor )
145
148
. style ( 'stroke-width' , 0 ) ;
146
149
}
@@ -165,7 +168,7 @@ exports.lsInner = function(gd) {
165
168
'height' : ya . _length
166
169
} ) ;
167
170
168
- plotinfo . plot . call ( Drawing . setTranslate , xa . _offset , ya . _offset ) ;
171
+ Drawing . setTranslate ( plotinfo . plot , xa . _offset , ya . _offset ) ;
169
172
170
173
var plotClipId ;
171
174
var layerClipId ;
@@ -191,126 +194,153 @@ exports.lsInner = function(gd) {
191
194
// to DRY up Drawing.setClipUrl calls downstream
192
195
plotinfo . layerClipId = layerClipId ;
193
196
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
+ }
234
264
235
265
// save axis line positions for ticks, draggers, etc to reference
236
266
// each subplot gets an entry:
237
267
// [left or bottom, right or top, free, main]
238
268
// main is the position at which to draw labels and draggers, if any
239
269
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
243
273
] ;
244
- if ( xa . anchor === ya . _id ) {
274
+ if ( xa . _anchorAxis === ya ) {
245
275
xa . _linepositions [ subplot ] [ 3 ] = xa . side === 'top' ?
246
- toppos : bottompos ;
276
+ xLinesYTop : xLinesYBottom ;
247
277
}
248
- else if ( showfreex ) {
249
- xa . _linepositions [ subplot ] [ 3 ] = freeposx ;
278
+ else if ( showFreeX ) {
279
+ xa . _linepositions [ subplot ] [ 3 ] = xLinesYFree ;
250
280
}
251
281
252
282
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
256
286
] ;
257
- if ( ya . anchor === xa . _id ) {
287
+ if ( ya . _anchorAxis === xa ) {
258
288
ya . _linepositions [ subplot ] [ 3 ] = ya . side === 'right' ?
259
- rightpos : leftpos ;
289
+ yLinesXRight : yLinesXLeft ;
260
290
}
261
- else if ( showfreey ) {
262
- ya . _linepositions [ subplot ] [ 3 ] = freeposy ;
291
+ else if ( showFreeY ) {
292
+ ya . _linepositions [ subplot ] [ 3 ] = yLinesXFree ;
263
293
}
264
294
265
295
// translate all the extra stuff to have the
266
296
// 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 ;
274
304
}
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 ;
279
309
}
280
310
281
- if ( fullLayout . _has ( 'cartesian' ) ) {
311
+ if ( hasSVGCartesian ) {
282
312
plotinfo . xlines
283
- . attr ( 'transform' , originx )
313
+ . attr ( 'transform' , originX )
284
314
. 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 ) ) ||
288
318
// so it doesn't barf with no lines shown
289
319
'M0,0' )
290
320
. style ( 'stroke-width' , xlw + 'px' )
291
321
. call ( Color . stroke , xa . showline ?
292
322
xa . linecolor : 'rgba(0,0,0,0)' ) ;
293
323
plotinfo . ylines
294
- . attr ( 'transform' , originy )
324
+ . attr ( 'transform' , originY )
295
325
. 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 ) ) ||
299
329
'M0,0' )
300
- . attr ( 'stroke-width' , ylw + 'px' )
330
+ . style ( 'stroke-width' , ylw + 'px' )
301
331
. call ( Color . stroke , ya . showline ?
302
332
ya . linecolor : 'rgba(0,0,0,0)' ) ;
303
333
}
304
334
305
- plotinfo . xaxislayer . attr ( 'transform' , originx ) ;
306
- plotinfo . yaxislayer . attr ( 'transform' , originy ) ;
335
+ plotinfo . xaxislayer . attr ( 'transform' , originX ) ;
336
+ plotinfo . yaxislayer . attr ( 'transform' , originY ) ;
307
337
plotinfo . gridlayer . attr ( 'transform' , origin ) ;
308
338
plotinfo . zerolinelayer . attr ( 'transform' , origin ) ;
309
339
plotinfo . draglayer . attr ( 'transform' , origin ) ;
310
340
311
341
// 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 ;
314
344
} ) ;
315
345
316
346
Plotly . Axes . makeClipPaths ( gd ) ;
@@ -320,6 +350,65 @@ exports.lsInner = function(gd) {
320
350
return gd . _promises . length && Promise . all ( gd . _promises ) ;
321
351
} ;
322
352
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
+
323
412
exports . drawMainTitle = function ( gd ) {
324
413
var fullLayout = gd . _fullLayout ;
325
414
0 commit comments