@@ -13,7 +13,10 @@ var d3 = require('d3');
1313var isNumeric = require ( 'fast-isnumeric' ) ;
1414
1515var Lib = require ( '../../lib' ) ;
16+ var svgTextUtils = require ( '../../lib/svg_text_utils' ) ;
17+
1618var Color = require ( '../../components/color' ) ;
19+ var Drawing = require ( '../../components/drawing' ) ;
1720var ErrorBars = require ( '../../components/errorbars' ) ;
1821
1922var arraysToCalcdata = require ( './arrays_to_calcdata' ) ;
@@ -42,9 +45,9 @@ module.exports = function plot(gd, plotinfo, cdbar) {
4245
4346 arraysToCalcdata ( d ) ;
4447
45- d3 . select ( this ) . selectAll ( 'path ' )
48+ d3 . select ( this ) . selectAll ( 'g.point ' )
4649 . data ( Lib . identity )
47- . enter ( ) . append ( 'path' )
50+ . enter ( ) . append ( 'g' ) . classed ( 'point' , true )
4851 . each ( function ( di , i ) {
4952 // now display the bar
5053 // clipped xf/yf (2nd arg true): non-positive
@@ -75,15 +78,18 @@ module.exports = function plot(gd, plotinfo, cdbar) {
7578 d3 . select ( this ) . remove ( ) ;
7679 return ;
7780 }
81+
7882 var lw = ( di . mlw + 1 || trace . marker . line . width + 1 ||
7983 ( di . trace ? di . trace . marker . line . width : 0 ) + 1 ) - 1 ,
8084 offset = d3 . round ( ( lw / 2 ) % 1 , 2 ) ;
85+
8186 function roundWithLine ( v ) {
8287 // if there are explicit gaps, don't round,
8388 // it can make the gaps look crappy
8489 return ( fullLayout . bargap === 0 && fullLayout . bargroupgap === 0 ) ?
8590 d3 . round ( Math . round ( v ) - offset , 2 ) : v ;
8691 }
92+
8793 function expandToVisible ( v , vc ) {
8894 // if it's not in danger of disappearing entirely,
8995 // round more precisely
@@ -93,6 +99,7 @@ module.exports = function plot(gd, plotinfo, cdbar) {
9399 // its neighbor
94100 ( v > vc ? Math . ceil ( v ) : Math . floor ( v ) ) ;
95101 }
102+
96103 if ( ! gd . _context . staticPlot ) {
97104 // if bars are not fully opaque or they have a line
98105 // around them, round to integer pixels, mainly for
@@ -108,12 +115,274 @@ module.exports = function plot(gd, plotinfo, cdbar) {
108115 y0 = fixpx ( y0 , y1 ) ;
109116 y1 = fixpx ( y1 , y0 ) ;
110117 }
111- d3 . select ( this ) . attr ( 'd' ,
118+
119+ // append bar path and text
120+ var bar = d3 . select ( this ) ;
121+
122+ bar . append ( 'path' ) . attr ( 'd' ,
112123 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z' ) ;
124+
125+ appendBarText ( gd , bar , d , i , x0 , x1 , y0 , y1 ) ;
113126 } ) ;
114127 } ) ;
115128
116129 // error bars are on the top
117130 bartraces . call ( ErrorBars . plot , plotinfo ) ;
118131
119132} ;
133+
134+ function appendBarText ( gd , bar , calcTrace , i , x0 , x1 , y0 , y1 ) {
135+ var trace = calcTrace [ 0 ] . trace ;
136+
137+ // get bar text
138+ var traceText = trace . text ;
139+ if ( ! traceText ) return ;
140+
141+ var text ;
142+ if ( Array . isArray ( traceText ) ) {
143+ if ( i >= traceText . length ) return ;
144+ text = traceText [ i ] ;
145+ }
146+ else {
147+ text = traceText ;
148+ }
149+
150+ // get text position
151+ var traceTextPosition = trace . textposition ,
152+ textPosition ;
153+ if ( Array . isArray ( traceTextPosition ) ) {
154+ if ( i >= traceTextPosition . length ) return ;
155+ textPosition = traceTextPosition [ i ] ;
156+ }
157+ else {
158+ textPosition = traceTextPosition ;
159+ }
160+
161+ if ( textPosition === 'none' ) return ;
162+
163+ var barWidth = Math . abs ( x1 - x0 ) ,
164+ barHeight = Math . abs ( y1 - y0 ) ,
165+ barIsTooSmall = ( barWidth < 8 || barHeight < 8 ) ,
166+
167+ barmode = gd . _fullLayout . barmode ,
168+ inStackMode = ( barmode === 'stack' ) ,
169+ inRelativeMode = ( barmode === 'relative' ) ,
170+ inStackOrRelativeMode = inStackMode || inRelativeMode ,
171+
172+ calcBar = calcTrace [ i ] ,
173+ isOutmostBar = ! inStackOrRelativeMode || calcBar . _outmost ;
174+
175+ if ( textPosition === 'auto' ) {
176+ textPosition = ( barIsTooSmall && isOutmostBar ) ? 'outside' : 'inside' ;
177+ }
178+
179+ if ( textPosition === 'outside' ) {
180+ if ( ! isOutmostBar ) textPosition = 'inside' ;
181+ }
182+
183+ if ( textPosition === 'inside' ) {
184+ if ( barIsTooSmall ) return ;
185+ }
186+
187+
188+ // get text font
189+ var textFont ;
190+
191+ if ( textPosition === 'outside' ) {
192+ var traceOutsideTextFont = trace . outsidetextfont ;
193+ if ( Array . isArray ( traceOutsideTextFont ) ) {
194+ if ( i >= traceOutsideTextFont . length ) return ;
195+ textFont = traceOutsideTextFont [ i ] ;
196+ }
197+ else {
198+ textFont = traceOutsideTextFont ;
199+ }
200+ }
201+ else {
202+ var traceInsideTextFont = trace . insidetextfont ;
203+ if ( Array . isArray ( traceInsideTextFont ) ) {
204+ if ( i >= traceInsideTextFont . length ) return ;
205+ textFont = traceInsideTextFont [ i ] ;
206+ }
207+ else {
208+ textFont = traceInsideTextFont ;
209+ }
210+ }
211+
212+ if ( ! textFont ) {
213+ var traceTextFont = trace . textfont ;
214+ if ( Array . isArray ( traceTextFont ) ) {
215+ if ( i >= traceTextFont . length ) return ;
216+ textFont = traceTextFont [ i ] ;
217+ }
218+ else {
219+ textFont = traceTextFont ;
220+ }
221+ }
222+
223+ if ( ! textFont ) {
224+ textFont = gd . _fullLayout . font ;
225+ }
226+
227+ // append bar text
228+ var textSelection = bar . append ( 'text' )
229+ // prohibit tex interpretation until we can handle
230+ // tex and regular text together
231+ . attr ( 'data-notex' , 1 )
232+ . text ( text )
233+ . attr ( {
234+ 'class' : 'bartext' ,
235+ transform : '' ,
236+ 'data-bb' : '' ,
237+ 'text-anchor' : 'middle' ,
238+ x : 0 ,
239+ y : 0
240+ } )
241+ . call ( Drawing . font , textFont ) ;
242+
243+ textSelection . call ( svgTextUtils . convertToTspans ) ;
244+ textSelection . selectAll ( 'tspan.line' ) . attr ( { x : 0 , y : 0 } ) ;
245+
246+ // position bar text
247+ var textBB = Drawing . bBox ( textSelection . node ( ) ) ,
248+ textWidth = textBB . width ,
249+ textHeight = textBB . height ;
250+ if ( ! textWidth || ! textHeight ) {
251+ textSelection . remove ( ) ;
252+ return ;
253+ }
254+
255+ // compute translate transform
256+ var transform ;
257+ if ( textPosition === 'outside' ) {
258+ transform = getTransformToMoveOutsideBar ( x0 , x1 , y0 , y1 , textBB ,
259+ trace . orientation ) ;
260+ }
261+ else {
262+ transform = getTransformToMoveInsideBar ( x0 , x1 , y0 , y1 , textBB ) ;
263+ }
264+
265+ textSelection . attr ( 'transform' , transform ) ;
266+ }
267+
268+ function getTransformToMoveInsideBar ( x0 , x1 , y0 , y1 , textBB ) {
269+ // compute text and target positions
270+ var barWidth = Math . abs ( x1 - x0 ) ,
271+ barHeight = Math . abs ( y1 - y0 ) ,
272+ textWidth = textBB . width ,
273+ textHeight = textBB . height ,
274+ barX = ( x0 + x1 ) / 2 ,
275+ barY = ( y0 + y1 ) / 2 ,
276+ textX = ( textBB . left + textBB . right ) / 2 ,
277+ textY = ( textBB . top + textBB . bottom ) / 2 ;
278+
279+ // apply target padding
280+ var targetWidth = 0.95 * barWidth ,
281+ targetHeight = 0.95 * barHeight ;
282+
283+ return getTransform (
284+ textX , textY , textWidth , textHeight ,
285+ barX , barY , targetWidth , targetHeight ) ;
286+ }
287+
288+ function getTransformToMoveOutsideBar ( x0 , x1 , y0 , y1 , textBB , orientation ) {
289+
290+ // compute text and target positions
291+ var textWidth = textBB . width ,
292+ textHeight = textBB . height ,
293+ textX = ( textBB . left + textBB . right ) / 2 ,
294+ textY = ( textBB . top + textBB . bottom ) / 2 ;
295+
296+ var targetWidth , targetHeight ,
297+ targetX , targetY ;
298+ if ( orientation === 'h' ) {
299+ if ( x1 < x0 ) {
300+ // bar end is on the left hand side
301+ targetWidth = 2 + textWidth ; // padding included
302+ targetHeight = Math . abs ( y1 - y0 ) ;
303+ targetX = x1 - targetWidth / 2 ;
304+ targetY = ( y0 + y1 ) / 2 ;
305+ }
306+ else {
307+ targetWidth = 2 + textWidth ; // padding included
308+ targetHeight = Math . abs ( y1 - y0 ) ;
309+ targetX = x1 + targetWidth / 2 ;
310+ targetY = ( y0 + y1 ) / 2 ;
311+ }
312+ }
313+ else {
314+ if ( y1 > y0 ) {
315+ // bar end is on the bottom
316+ targetWidth = Math . abs ( x1 - x0 ) ;
317+ targetHeight = 2 + textHeight ; // padding included
318+ targetX = ( x0 + x1 ) / 2 ;
319+ targetY = y1 + targetHeight / 2 ;
320+ }
321+ else {
322+ targetWidth = Math . abs ( x1 - x0 ) ;
323+ targetHeight = 2 + textHeight ; // padding included
324+ targetX = ( x0 + x1 ) / 2 ;
325+ targetY = y1 - targetHeight / 2 ;
326+ }
327+ }
328+
329+ return getTransform (
330+ textX , textY , textWidth , textHeight ,
331+ targetX , targetY , targetWidth , targetHeight ) ;
332+ }
333+
334+ /**
335+ * Compute SVG transform to move a text box into a target box
336+ *
337+ * @param {number } textX X pixel coord of the text box center
338+ * @param {number } textY Y pixel coord of the text box center
339+ * @param {number } textWidth text box width
340+ * @param {number } textHeight text box height
341+ * @param {number } targetX X pixel coord of the target box center
342+ * @param {number } targetY Y pixel coord of the target box center
343+ * @param {number } targetWidth target box width
344+ * @param {number } targetHeight target box height
345+ *
346+ * @returns {string } SVG transform
347+ */
348+ function getTransform (
349+ textX , textY , textWidth , textHeight ,
350+ targetX , targetY , targetWidth , targetHeight ) {
351+
352+ // compute translate transform
353+ var translateX = targetX - textX ,
354+ translateY = targetY - textY ,
355+ translate = 'translate(' + translateX + ' ' + translateY + ')' ;
356+
357+ // if bar text doesn't fit, compute rotate and scale transforms
358+ var doesntFit = ( textWidth > targetWidth || textHeight > targetHeight ) ,
359+ rotate , scale , scaleX , scaleY ;
360+
361+ if ( doesntFit ) {
362+ var textIsHorizontal = ( textWidth > textHeight ) ,
363+ targetIsHorizontal = ( targetWidth > targetHeight ) ;
364+ if ( textIsHorizontal !== targetIsHorizontal ) {
365+ rotate = 'rotate(-90 ' + textX + ' ' + textY + ')' ;
366+ scaleX = targetWidth / textHeight ;
367+ scaleY = targetHeight / textWidth ;
368+ }
369+ else {
370+ scaleX = targetWidth / textWidth ;
371+ scaleY = targetHeight / textHeight ;
372+ }
373+
374+ if ( scaleX > 1 ) scaleX = 1 ;
375+ if ( scaleY > 1 ) scaleY = 1 ;
376+
377+ if ( scaleX !== 1 || scaleY !== 1 ) {
378+ scale = 'scale(' + scaleX + ' ' + scaleY + ')' ;
379+ }
380+ }
381+
382+ // compute transform
383+ var transform = translate ;
384+ if ( scale ) transform += ' ' + scale ;
385+ if ( rotate ) transform += ' ' + rotate ;
386+
387+ return transform ;
388+ }
0 commit comments