@@ -22,8 +22,6 @@ function crossTraceCalc(gd, plotinfo) {
2222 var orientation = orientations [ i ] ;
2323 var posAxis = orientation === 'h' ? ya : xa ;
2424 var boxList = [ ] ;
25- var minPad = 0 ;
26- var maxPad = 0 ;
2725
2826 // make list of boxes / candlesticks
2927 // For backward compatibility, candlesticks are treated as if they *are* box traces here
@@ -40,72 +38,173 @@ function crossTraceCalc(gd, plotinfo) {
4038 trace . yaxis === ya . _id
4139 ) {
4240 boxList . push ( j ) ;
43-
44- if ( trace . boxpoints ) {
45- minPad = Math . max ( minPad , trace . jitter - trace . pointpos - 1 ) ;
46- maxPad = Math . max ( maxPad , trace . jitter + trace . pointpos - 1 ) ;
47- }
4841 }
4942 }
5043
51- setPositionOffset ( 'box' , gd , boxList , posAxis , [ minPad , maxPad ] ) ;
44+ setPositionOffset ( 'box' , gd , boxList , posAxis ) ;
5245 }
5346}
5447
55- function setPositionOffset ( traceType , gd , boxList , posAxis , pad ) {
48+ function setPositionOffset ( traceType , gd , boxList , posAxis ) {
5649 var calcdata = gd . calcdata ;
5750 var fullLayout = gd . _fullLayout ;
58- var pointList = [ ] ;
51+ var axId = posAxis . _id ;
52+ var axLetter = axId . charAt ( 0 ) ;
5953
6054 // N.B. reused in violin
6155 var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes' ;
6256
6357 var i , j , calcTrace ;
58+ var pointList = [ ] ;
59+ var shownPts = 0 ;
6460
6561 // make list of box points
6662 for ( i = 0 ; i < boxList . length ; i ++ ) {
6763 calcTrace = calcdata [ boxList [ i ] ] ;
6864 for ( j = 0 ; j < calcTrace . length ; j ++ ) {
6965 pointList . push ( calcTrace [ j ] . pos ) ;
66+ shownPts += ( calcTrace [ j ] . pts2 || [ ] ) . length ;
7067 }
7168 }
7269
7370 if ( ! pointList . length ) return ;
7471
7572 // box plots - update dPos based on multiple traces
76- // and then use for posAxis autorange
7773 var boxdv = Lib . distinctVals ( pointList ) ;
78- var dPos = boxdv . minDiff / 2 ;
79-
80- // if there's no duplication of x points,
81- // disable 'group' mode by setting counter to 1
82- if ( pointList . length === boxdv . vals . length ) {
83- fullLayout [ numKey ] = 1 ;
84- }
74+ var dPos0 = boxdv . minDiff / 2 ;
8575
8676 // check for forced minimum dtick
8777 Axes . minDtick ( posAxis , boxdv . minDiff , boxdv . vals [ 0 ] , true ) ;
8878
89- var gap = fullLayout [ traceType + 'gap' ] ;
90- var groupgap = fullLayout [ traceType + 'groupgap' ] ;
91- var padfactor = ( 1 - gap ) * ( 1 - groupgap ) * dPos / fullLayout [ numKey ] ;
92-
93- // autoscale the x axis - including space for points if they're off the side
94- // TODO: this will overdo it if the outermost boxes don't have
95- // their points as far out as the other boxes
96- var extremes = Axes . findExtremes ( posAxis , boxdv . vals , {
97- vpadminus : dPos + pad [ 0 ] * padfactor ,
98- vpadplus : dPos + pad [ 1 ] * padfactor
99- } ) ;
79+ var num = fullLayout [ numKey ] ;
80+ var group = ( fullLayout [ traceType + 'mode' ] === 'group' && num > 1 ) ;
81+ var groupFraction = 1 - fullLayout [ traceType + 'gap' ] ;
82+ var groupGapFraction = 1 - fullLayout [ traceType + 'groupgap' ] ;
10083
10184 for ( i = 0 ; i < boxList . length ; i ++ ) {
10285 calcTrace = calcdata [ boxList [ i ] ] ;
103- // set the width of all boxes
104- calcTrace [ 0 ] . t . dPos = dPos ;
105- // link extremes to all boxes
106- calcTrace [ 0 ] . trace . _extremes [ posAxis . _id ] = extremes ;
107- }
10886
87+ var trace = calcTrace [ 0 ] . trace ;
88+ var t = calcTrace [ 0 ] . t ;
89+ var width = trace . width ;
90+ var side = trace . side ;
91+
92+ // position coordinate delta
93+ var dPos ;
94+ // box half width;
95+ var bdPos ;
96+ // box center offset
97+ var bPos ;
98+ // half-width within which to accept hover for this box/violin
99+ // always split the distance to the closest box/violin
100+ var wHover ;
101+
102+ if ( width ) {
103+ dPos = bdPos = wHover = width / 2 ;
104+ bPos = 0 ;
105+ } else {
106+ dPos = dPos0 ;
107+ bdPos = dPos * groupFraction * groupGapFraction / ( group ? num : 1 ) ;
108+ bPos = group ? 2 * dPos * ( - 0.5 + ( t . num + 0.5 ) / num ) * groupFraction : 0 ;
109+ wHover = dPos * ( group ? groupFraction / num : 1 ) ;
110+ }
111+ t . dPos = dPos ;
112+ t . bPos = bPos ;
113+ t . bdPos = bdPos ;
114+ t . wHover = wHover ;
115+
116+ // box/violin-only value-space push value
117+ var pushplus ;
118+ var pushminus ;
119+ // edge of box/violin
120+ var edge = bPos + bdPos ;
121+ var edgeplus ;
122+ var edgeminus ;
123+
124+ if ( side === 'positive' ) {
125+ pushplus = dPos * ( width ? 1 : 0.5 ) ;
126+ edgeplus = edge ;
127+ pushminus = edgeplus = bPos ;
128+ } else if ( side === 'negative' ) {
129+ pushplus = edgeplus = bPos ;
130+ pushminus = dPos * ( width ? 1 : 0.5 ) ;
131+ edgeminus = edge ;
132+ } else {
133+ pushplus = pushminus = dPos ;
134+ edgeplus = edgeminus = edge ;
135+ }
136+
137+ // value-space padding
138+ var vpadplus ;
139+ var vpadminus ;
140+ // pixel-space padding
141+ var ppadplus ;
142+ var ppadminus ;
143+ // do we add 5% of both sides (for points beyond box/violin)
144+ var padded = false ;
145+ // does this trace show points?
146+ var hasPts = ( trace . boxpoints || trace . points ) && ( shownPts > 0 ) ;
147+
148+ if ( hasPts ) {
149+ var pointpos = trace . pointpos ;
150+ var jitter = trace . jitter ;
151+ var ms = trace . marker . size / 2 ;
152+
153+ var pp = 0 ;
154+ if ( ( pointpos + jitter ) >= 0 ) {
155+ pp = edge * ( pointpos + jitter ) ;
156+ if ( pp > pushplus ) {
157+ // (++) beyond plus-value, use pp
158+ padded = true ;
159+ ppadplus = ms ;
160+ vpadplus = pp ;
161+ } else if ( pp > edgeplus ) {
162+ // (+), use push-value (it's bigger), but add px-pad
163+ ppadplus = ms ;
164+ vpadplus = pushplus ;
165+ }
166+ }
167+ if ( pp <= pushplus ) {
168+ // (->) fallback to push value
169+ vpadplus = pushplus ;
170+ }
171+
172+ var pm = 0 ;
173+ if ( ( pointpos - jitter ) <= 0 ) {
174+ pm = - edge * ( pointpos - jitter ) ;
175+ if ( pm > pushminus ) {
176+ // (--) beyond plus-value, use pp
177+ padded = true ;
178+ ppadminus = ms ;
179+ vpadminus = pm ;
180+ } else if ( pm > edgeminus ) {
181+ // (-), use push-value (it's bigger), but add px-pad
182+ ppadminus = ms ;
183+ vpadminus = pushminus ;
184+ }
185+ }
186+ if ( pm <= pushminus ) {
187+ // (<-) fallback to push value
188+ vpadminus = pushminus ;
189+ }
190+ } else {
191+ vpadplus = pushplus ;
192+ vpadminus = pushminus ;
193+ }
194+
195+ // calcdata[i][j] are in ascending order
196+ var firstPos = calcTrace [ 0 ] . pos ;
197+ var lastPos = calcTrace [ calcTrace . length - 1 ] . pos ;
198+
199+ trace . _extremes [ axId ] = Axes . findExtremes ( posAxis , [ firstPos , lastPos ] , {
200+ padded : padded ,
201+ vpadminus : vpadminus ,
202+ vpadplus : vpadplus ,
203+ // N.B. SVG px-space positive/negative
204+ ppadminus : { x : ppadminus , y : ppadplus } [ axLetter ] ,
205+ ppadplus : { x : ppadplus , y : ppadminus } [ axLetter ] ,
206+ } ) ;
207+ }
109208}
110209
111210module . exports = {
0 commit comments