@@ -177,7 +177,7 @@ drawing.dashStyle = function(dash, lineWidth) {
177177 return dash ;
178178} ;
179179
180- function setFillStyle ( sel , trace , gd ) {
180+ function setFillStyle ( sel , trace , gd , forLegend ) {
181181 var markerPattern = trace . fillpattern ;
182182 var patternShape = markerPattern && drawing . getPatternAttr ( markerPattern . shape , 0 , '' ) ;
183183 if ( patternShape ) {
@@ -192,6 +192,55 @@ function setFillStyle(sel, trace, gd) {
192192 undefined , markerPattern . fillmode ,
193193 patternBGColor , patternFGColor , patternFGOpacity
194194 ) ;
195+ } else if ( trace . fillgradient && trace . fillgradient . orientation !== 'none' ) {
196+ var direction = trace . fillgradient . orientation ;
197+ var gradientID = 'scatterfill-' + trace . uid ;
198+ if ( forLegend ) {
199+ gradientID = 'legendfill-' + trace . uid ;
200+ }
201+
202+ if ( ! forLegend && ( trace . fillgradient . start !== undefined || trace . fillgradient . stop !== undefined ) ) {
203+ var start , stop ;
204+ if ( direction === 'horizontal' ) {
205+ start = {
206+ x : trace . fillgradient . start ,
207+ y : 0 ,
208+ } ;
209+ stop = {
210+ x : trace . fillgradient . stop ,
211+ y : 0 ,
212+ } ;
213+ } else if ( direction === 'vertical' ) {
214+ start = {
215+ x : 0 ,
216+ y : trace . fillgradient . start ,
217+ } ;
218+ stop = {
219+ x : 0 ,
220+ y : trace . fillgradient . stop ,
221+ } ;
222+ }
223+
224+ start . x = trace . _xA . c2p (
225+ ( start . x === undefined ) ? trace . _extremes . x . min [ 0 ] . val : start . x , true
226+ ) ;
227+ start . y = trace . _yA . c2p (
228+ ( start . y === undefined ) ? trace . _extremes . y . min [ 0 ] . val : start . y , true
229+ ) ;
230+
231+ stop . x = trace . _xA . c2p (
232+ ( stop . x === undefined ) ? trace . _extremes . x . max [ 0 ] . val : stop . x , true
233+ ) ;
234+ stop . y = trace . _yA . c2p (
235+ ( stop . y === undefined ) ? trace . _extremes . y . max [ 0 ] . val : stop . y , true
236+ ) ;
237+ sel . call ( gradientWithBounds , gd , gradientID , 'linear' , trace . fillgradient . colorscale , 'fill' , start , stop , true , false ) ;
238+ } else {
239+ if ( direction === 'horizontal' ) {
240+ direction = direction + 'reversed' ;
241+ }
242+ sel . call ( drawing . gradient , gd , gradientID , direction , trace . fillgradient . colorscale , 'fill' ) ;
243+ }
195244 } else if ( trace . fillcolor ) {
196245 sel . call ( Color . fill , trace . fillcolor ) ;
197246 }
@@ -202,17 +251,17 @@ drawing.singleFillStyle = function(sel, gd) {
202251 var node = d3 . select ( sel . node ( ) ) ;
203252 var data = node . data ( ) ;
204253 var trace = ( ( data [ 0 ] || [ ] ) [ 0 ] || { } ) . trace || { } ;
205- setFillStyle ( sel , trace , gd ) ;
254+ setFillStyle ( sel , trace , gd , false ) ;
206255} ;
207256
208- drawing . fillGroupStyle = function ( s , gd ) {
257+ drawing . fillGroupStyle = function ( s , gd , forLegend ) {
209258 s . style ( 'stroke-width' , 0 )
210259 . each ( function ( d ) {
211260 var shape = d3 . select ( this ) ;
212261 // N.B. 'd' won't be a calcdata item when
213262 // fill !== 'none' on a segment-less and marker-less trace
214263 if ( d [ 0 ] . trace ) {
215- setFillStyle ( shape , d [ 0 ] . trace , gd ) ;
264+ setFillStyle ( shape , d [ 0 ] . trace , gd , forLegend ) ;
216265 }
217266 } ) ;
218267} ;
@@ -294,16 +343,14 @@ function makePointPath(symbolNumber, r, t, s) {
294343 return drawing . symbolFuncs [ base ] ( r , t , s ) + ( symbolNumber >= 200 ? DOTPATH : '' ) ;
295344}
296345
297- var HORZGRADIENT = { x1 : 1 , x2 : 0 , y1 : 0 , y2 : 0 } ;
298- var VERTGRADIENT = { x1 : 0 , x2 : 0 , y1 : 1 , y2 : 0 } ;
299346var stopFormatter = numberFormat ( '~f' ) ;
300347var gradientInfo = {
301- radial : { node : 'radialGradient ' } ,
302- radialreversed : { node : 'radialGradient ' , reversed : true } ,
303- horizontal : { node : 'linearGradient ' , attrs : HORZGRADIENT } ,
304- horizontalreversed : { node : 'linearGradient ' , attrs : HORZGRADIENT , reversed : true } ,
305- vertical : { node : 'linearGradient ' , attrs : VERTGRADIENT } ,
306- verticalreversed : { node : 'linearGradient ' , attrs : VERTGRADIENT , reversed : true }
348+ radial : { type : 'radial ' } ,
349+ radialreversed : { type : 'radial ' , reversed : true } ,
350+ horizontal : { type : 'linear ' , start : { x : 1 , y : 0 } , stop : { x : 0 , y : 0 } } ,
351+ horizontalreversed : { type : 'linear ' , start : { x : 1 , y : 0 } , stop : { x : 0 , y : 0 } , reversed : true } ,
352+ vertical : { type : 'linear ' , start : { x : 0 , y : 1 } , stop : { x : 0 , y : 0 } } ,
353+ verticalreversed : { type : 'linear ' , start : { x : 0 , y : 1 } , stop : { x : 0 , y : 0 } , reversed : true }
307354} ;
308355
309356/**
@@ -321,8 +368,57 @@ var gradientInfo = {
321368 * @param {string } prop: the property to apply to, 'fill' or 'stroke'
322369 */
323370drawing . gradient = function ( sel , gd , gradientID , type , colorscale , prop ) {
324- var len = colorscale . length ;
325371 var info = gradientInfo [ type ] ;
372+ return gradientWithBounds (
373+ sel , gd , gradientID , info . type , colorscale , prop , info . start , info . stop , false , info . reversed
374+ ) ;
375+ } ;
376+
377+ /**
378+ * gradient_with_bounds: create and apply a gradient fill for defined start and stop positions
379+ *
380+ * @param {object } sel: d3 selection to apply this gradient to
381+ * You can use `selection.call(Drawing.gradient, ...)`
382+ * @param {DOM element } gd: the graph div `sel` is part of
383+ * @param {string } gradientID: a unique (within this plot) identifier
384+ * for this gradient, so that we don't create unnecessary definitions
385+ * @param {string } type: 'radial' or 'linear'. Radial goes center to edge,
386+ * horizontal goes as defined by start and stop
387+ * @param {array } colorscale: as in attribute values, [[fraction, color], ...]
388+ * @param {string } prop: the property to apply to, 'fill' or 'stroke'
389+ * @param {object } start: start point for linear gradients, { x: number, y: number }.
390+ * Ignored if type is 'radial'.
391+ * @param {object } stop: stop point for linear gradients, { x: number, y: number }.
392+ * Ignored if type is 'radial'.
393+ * @param {boolean } inUserSpace: If true, start and stop give absolute values in the plot.
394+ * If false, start and stop are fractions of the traces extent along each axis.
395+ * @param {boolean } reversed: If true, the gradient is reversed between normal start and stop,
396+ * i.e., the colorscale is applied in order from stop to start for linear, from edge
397+ * to center for radial gradients.
398+ */
399+ function gradientWithBounds ( sel , gd , gradientID , type , colorscale , prop , start , stop , inUserSpace , reversed ) {
400+ var len = colorscale . length ;
401+
402+ var info ;
403+ if ( type === 'linear' ) {
404+ info = {
405+ node : 'linearGradient' ,
406+ attrs : {
407+ x1 : start . x ,
408+ y1 : start . y ,
409+ x2 : stop . x ,
410+ y2 : stop . y ,
411+ gradientUnits : inUserSpace ? 'userSpaceOnUse' : 'objectBoundingBox' ,
412+ } ,
413+ reversed : reversed ,
414+ } ;
415+ } else if ( type === 'radial' ) {
416+ info = {
417+ node : 'radialGradient' ,
418+ reversed : reversed ,
419+ } ;
420+ }
421+
326422 var colorStops = new Array ( len ) ;
327423 for ( var i = 0 ; i < len ; i ++ ) {
328424 if ( info . reversed ) {
@@ -368,7 +464,7 @@ drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) {
368464 . style ( prop + '-opacity' , null ) ;
369465
370466 sel . classed ( 'gradient_filled' , true ) ;
371- } ;
467+ }
372468
373469/**
374470 * pattern: create and apply a pattern fill
0 commit comments