@@ -272,136 +272,197 @@ function appendBarText(gd, bar, calcTrace, i, x0, x1, y0, y1) {
272272 }
273273 }
274274
275- // compute translate transform
275+ // set text transform
276276 var transform ;
277277 if ( textPosition === 'outside' ) {
278278 transform = getTransformToMoveOutsideBar ( x0 , x1 , y0 , y1 , textBB ,
279279 trace . orientation ) ;
280280 }
281281 else {
282- transform = getTransformToMoveInsideBar ( x0 , x1 , y0 , y1 , textBB ) ;
282+ transform = getTransformToMoveInsideBar ( x0 , x1 , y0 , y1 , textBB ,
283+ trace . orientation ) ;
283284 }
284285
285286 textSelection . attr ( 'transform' , transform ) ;
286287}
287288
288- function getTransformToMoveInsideBar ( x0 , x1 , y0 , y1 , textBB ) {
289+ function getTransformToMoveInsideBar ( x0 , x1 , y0 , y1 , textBB , orientation ) {
289290 // compute text and target positions
290- var barWidth = Math . abs ( x1 - x0 ) ,
291- barHeight = Math . abs ( y1 - y0 ) ,
292- textWidth = textBB . width ,
291+ var textWidth = textBB . width ,
293292 textHeight = textBB . height ,
294- barX = ( x0 + x1 ) / 2 ,
295- barY = ( y0 + y1 ) / 2 ,
296293 textX = ( textBB . left + textBB . right ) / 2 ,
297- textY = ( textBB . top + textBB . bottom ) / 2 ;
298-
299- // apply 3px target padding
300- var targetWidth = barWidth - 2 * TEXTPAD ,
301- targetHeight = barHeight - 2 * TEXTPAD ;
302-
303- return getTransform (
304- textX , textY , textWidth , textHeight ,
305- barX , barY , targetWidth , targetHeight ) ;
306- }
294+ textY = ( textBB . top + textBB . bottom ) / 2 ,
295+ barWidth = Math . abs ( x1 - x0 ) - 2 * TEXTPAD ,
296+ barHeight = Math . abs ( y1 - y0 ) - 2 * TEXTPAD ,
297+ targetWidth ,
298+ targetHeight ,
299+ targetX ,
300+ targetY ;
301+
302+ // compute rotation and scale
303+ var needsRotating ,
304+ scale ;
305+
306+ if ( textWidth <= barWidth && textHeight <= barHeight ) {
307+ // no scale or rotation is required
308+ needsRotating = false ;
309+ scale = 1 ;
310+ }
311+ else if ( textWidth <= barHeight && textHeight <= barWidth ) {
312+ // only rotation is required
313+ needsRotating = true ;
314+ scale = 1 ;
315+ }
316+ else if ( ( textWidth < textHeight ) === ( barWidth < barHeight ) ) {
317+ // only scale is required
318+ needsRotating = false ;
319+ scale = Math . min ( barWidth / textWidth , barHeight / textHeight ) ;
320+ }
321+ else {
322+ // both scale and rotation are required
323+ needsRotating = true ;
324+ scale = Math . min ( barHeight / textWidth , barWidth / textHeight ) ;
325+ }
307326
308- function getTransformToMoveOutsideBar ( x0 , x1 , y0 , y1 , textBB , orientation ) {
309327 // compute text and target positions
310- var textWidth = textBB . width ,
311- textHeight = textBB . height ,
312- textX = ( textBB . left + textBB . right ) / 2 ,
313- textY = ( textBB . top + textBB . bottom ) / 2 ;
328+ if ( needsRotating ) {
329+ targetWidth = scale * textHeight ;
330+ targetHeight = scale * textWidth ;
331+ }
332+ else {
333+ targetWidth = scale * textWidth ;
334+ targetHeight = scale * textHeight ;
335+ }
314336
315- var targetWidth , targetHeight ,
316- targetX , targetY ;
317337 if ( orientation === 'h' ) {
318338 if ( x1 < x0 ) {
319339 // bar end is on the left hand side
320- targetWidth = textWidth + 2 * TEXTPAD ; // padding included
321- targetHeight = Math . abs ( y1 - y0 ) - 2 * TEXTPAD ;
322- targetX = x1 - targetWidth / 2 ;
340+ targetX = x1 + TEXTPAD + targetWidth / 2 ;
323341 targetY = ( y0 + y1 ) / 2 ;
324342 }
325343 else {
326- targetWidth = textWidth + 2 * TEXTPAD ; // padding included
327- targetHeight = Math . abs ( y1 - y0 ) - 2 * TEXTPAD ;
328- targetX = x1 + targetWidth / 2 ;
344+ targetX = x1 - TEXTPAD - targetWidth / 2 ;
329345 targetY = ( y0 + y1 ) / 2 ;
330346 }
331347 }
332348 else {
333349 if ( y1 > y0 ) {
334350 // bar end is on the bottom
335- targetWidth = Math . abs ( x1 - x0 ) ;
336- targetHeight = 2 + textHeight ; // padding included
337351 targetX = ( x0 + x1 ) / 2 ;
338- targetY = y1 + targetHeight / 2 ;
352+ targetY = y1 - TEXTPAD - targetHeight / 2 ;
339353 }
340354 else {
341- targetWidth = Math . abs ( x1 - x0 ) ;
342- targetHeight = 2 + textHeight ; // padding included
343355 targetX = ( x0 + x1 ) / 2 ;
344- targetY = y1 - targetHeight / 2 ;
356+ targetY = y1 + TEXTPAD + targetHeight / 2 ;
345357 }
346358 }
347359
348- return getTransform (
349- textX , textY , textWidth , textHeight ,
350- targetX , targetY , targetWidth , targetHeight ) ;
360+ return getTransform ( textX , textY , targetX , targetY , scale , needsRotating ) ;
351361}
352362
353- /**
354- * Compute SVG transform to move a text box into a target box
355- *
356- * @param {number } textX X pixel coord of the text box center
357- * @param {number } textY Y pixel coord of the text box center
358- * @param {number } textWidth text box width
359- * @param {number } textHeight text box height
360- * @param {number } targetX X pixel coord of the target box center
361- * @param {number } targetY Y pixel coord of the target box center
362- * @param {number } targetWidth target box width
363- * @param {number } targetHeight target box height
364- *
365- * @returns {string } SVG transform
366- */
367- function getTransform (
368- textX , textY , textWidth , textHeight ,
369- targetX , targetY , targetWidth , targetHeight ) {
370-
371- // compute translate transform
372- var translateX = targetX - textX ,
373- translateY = targetY - textY ,
374- translate = 'translate(' + translateX + ' ' + translateY + ')' ;
375-
376- // if bar text doesn't fit, compute rotate and scale transforms
377- var doesntFit = ( textWidth > targetWidth || textHeight > targetHeight ) ,
378- rotate , scale , scaleX , scaleY ;
379-
380- if ( doesntFit ) {
381- var textIsHorizontal = ( textWidth > textHeight ) ,
382- targetIsHorizontal = ( targetWidth > targetHeight ) ;
383- if ( textIsHorizontal !== targetIsHorizontal ) {
384- rotate = 'rotate(-90 ' + textX + ' ' + textY + ')' ;
385- scaleX = targetWidth / textHeight ;
386- scaleY = targetHeight / textWidth ;
363+ function getTransformToMoveOutsideBar ( x0 , x1 , y0 , y1 , textBB , orientation ) {
364+ // In order to handle both orientations with the same algorithm,
365+ // *textWidth* is defined as the text length in the direction of *barWidth*.
366+ var barWidth ,
367+ textWidth ,
368+ textHeight ;
369+ if ( orientation === 'h' ) {
370+ barWidth = Math . abs ( y1 - y0 ) - 2 * TEXTPAD ;
371+ textWidth = textBB . height ;
372+ textHeight = textBB . width ;
373+ }
374+ else {
375+ barWidth = Math . abs ( x1 - x0 ) - 2 * TEXTPAD ;
376+ textWidth = textBB . width ;
377+ textHeight = textBB . height ;
378+ }
379+
380+ // compute rotation and scale
381+ var needsRotating ,
382+ scale ;
383+ if ( textWidth <= barWidth ) {
384+ // no scale or rotation
385+ needsRotating = false ;
386+ scale = 1 ;
387+ }
388+ else if ( textHeight <= textWidth ) {
389+ // only scale
390+ // (don't rotate to prevent having text perpendicular to the bar)
391+ needsRotating = false ;
392+ scale = barWidth / textWidth ;
393+ }
394+ else if ( textHeight <= barWidth ) {
395+ // only rotation
396+ needsRotating = true ;
397+ scale = 1 ;
398+ }
399+ else {
400+ // both scale and rotation
401+ // (rotation prevents having text perpendicular to the bar)
402+ needsRotating = true ;
403+ scale = barWidth / textHeight ;
404+ }
405+
406+ // compute text and target positions
407+ var textX = ( textBB . left + textBB . right ) / 2 ,
408+ textY = ( textBB . top + textBB . bottom ) / 2 ,
409+ targetWidth ,
410+ targetHeight ,
411+ targetX ,
412+ targetY ;
413+ if ( needsRotating ) {
414+ targetWidth = scale * textBB . height ;
415+ targetHeight = scale * textBB . width ;
416+ }
417+ else {
418+ targetWidth = scale * textBB . width ;
419+ targetHeight = scale * textBB . height ;
420+ }
421+
422+ if ( orientation === 'h' ) {
423+ if ( x1 < x0 ) {
424+ // bar end is on the left hand side
425+ targetX = x1 - TEXTPAD - targetWidth / 2 ;
426+ targetY = ( y0 + y1 ) / 2 ;
387427 }
388428 else {
389- scaleX = targetWidth / textWidth ;
390- scaleY = targetHeight / textHeight ;
429+ targetX = x1 + TEXTPAD + targetWidth / 2 ;
430+ targetY = ( y0 + y1 ) / 2 ;
391431 }
432+ }
433+ else {
434+ if ( y1 > y0 ) {
435+ // bar end is on the bottom
436+ targetX = ( x0 + x1 ) / 2 ;
437+ targetY = y1 + TEXTPAD + targetHeight / 2 ;
438+ }
439+ else {
440+ targetX = ( x0 + x1 ) / 2 ;
441+ targetY = y1 - TEXTPAD - targetHeight / 2 ;
442+ }
443+ }
392444
393- if ( scaleX > 1 ) scaleX = 1 ;
394- if ( scaleY > 1 ) scaleY = 1 ;
445+ return getTransform ( textX , textY , targetX , targetY , scale , needsRotating ) ;
446+ }
395447
396- if ( scaleX !== 1 || scaleY !== 1 ) {
397- scale = 'scale(' + scaleX + ' ' + scaleY + ')' ;
398- }
448+ function getTransform ( textX , textY , targetX , targetY , scale , needsRotating ) {
449+ var transformScale ,
450+ transformRotate ,
451+ transformTranslate ;
452+
453+ if ( scale < 1 ) transformScale = 'scale(' + scale + ') ' ;
454+ else {
455+ scale = 1 ;
456+ transformScale = '' ;
399457 }
400458
401- // compute transform
402- var transform = translate ;
403- if ( scale ) transform += ' ' + scale ;
404- if ( rotate ) transform += ' ' + rotate ;
459+ transformRotate = ( needsRotating ) ?
460+ 'rotate(-90 ' + textX + ' ' + textY + ') ' : '' ;
461+
462+ // Note that scaling also affects the center of the text box
463+ var translateX = ( targetX - scale * textX ) ,
464+ translateY = ( targetY - scale * textY ) ;
465+ transformTranslate = 'translate(' + translateX + ' ' + translateY + ')' ;
405466
406- return transform ;
467+ return transformTranslate + transformScale + transformRotate ;
407468}
0 commit comments