@@ -354,6 +354,145 @@ drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) {
354354 fullLayout . _gradientUrlQueryParts [ k ] = 1 ;
355355} ;
356356
357+ /**
358+ * pattern: create and apply a pattern fill
359+ *
360+ * @param {object } sel: d3 selection to apply this pattern to
361+ * You can use `selection.call(Drawing.pattern, ...)`
362+ * @param {DOM element } gd: the graph div `sel` is part of
363+ * @param {string } patternID: a unique (within this plot) identifier
364+ * for this pattern, so that we don't create unnecessary definitions
365+ * @param {string } bgcolor: background color for this pattern
366+ * @param {string } fgcolor: foreground color for this pattern
367+ * @param {number } scale: scale of this pattern
368+ * @param {number } solidity: how solid lines of this pattern are
369+ * @param {string } prop: the property to apply to, 'fill' or 'stroke'
370+ */
371+ drawing . pattern = function ( sel , gd , patternID , shape , bgcolor , fgcolor , scale , solidity , prop ) {
372+ var fullLayout = gd . _fullLayout ;
373+ var fullID = 'p' + fullLayout . _uid + '-' + patternID ;
374+ var baseSize = 8 * scale ;
375+ var width , height ;
376+
377+ var path = '' ;
378+ switch ( shape ) {
379+ case '/' :
380+ width = baseSize * Math . sqrt ( 2 ) ;
381+ height = baseSize * Math . sqrt ( 2 ) ;
382+ path = 'M-1,1l2,-2' +
383+ 'M0,' + height + 'L' + width + ',0' +
384+ 'M' + ( width - 1 ) + ',' + ( height + 1 ) + 'l2,-2' ;
385+ break ;
386+ case '\\' :
387+ width = baseSize * Math . sqrt ( 2 ) ;
388+ height = baseSize * Math . sqrt ( 2 ) ;
389+ path = 'M' + ( width - 1 ) + ',-1l2,2' +
390+ 'M0,0L' + width + ',' + height +
391+ 'M-1,' + ( height - 1 ) + 'l2,2' ;
392+ break ;
393+ case 'x' :
394+ width = baseSize * Math . sqrt ( 2 ) ;
395+ height = baseSize * Math . sqrt ( 2 ) ;
396+ path = 'M-1,1l2,-2' +
397+ 'M0,' + height + 'L' + width + ',0' +
398+ 'M' + ( width - 1 ) + ',' + ( height + 1 ) + 'l2,-2' +
399+ 'M' + ( width - 1 ) + ',-1l2,2' +
400+ 'M0,0L' + width + ',' + height +
401+ 'M-1,' + ( height - 1 ) + 'l2,2' ;
402+ break ;
403+ case '|' :
404+ width = baseSize ;
405+ height = baseSize ;
406+ path = 'M' + ( width / 2 ) + ',0L' + ( width / 2 ) + ',' + height ;
407+ break ;
408+ case '-' :
409+ width = baseSize ;
410+ height = baseSize ;
411+ path = 'M0,' + ( height / 2 ) + 'L' + width + ',' + ( height / 2 ) ;
412+ break ;
413+ case '+' :
414+ width = baseSize ;
415+ height = baseSize ;
416+ path = 'M' + ( width / 2 ) + ',0L' + ( width / 2 ) + ',' + height +
417+ 'M0,' + ( height / 2 ) + 'L' + width + ',' + ( height / 2 ) ;
418+ break ;
419+ case '+' :
420+ width = baseSize ;
421+ height = baseSize ;
422+ path = 'M' + ( width / 2 ) + ',0L' + ( width / 2 ) + ',' + height +
423+ 'M0,' + ( height / 2 ) + 'L' + width + ',' + ( height / 2 ) ;
424+ break ;
425+ case '.' :
426+ width = baseSize ;
427+ height = baseSize ;
428+ break ;
429+ }
430+
431+ var pattern = fullLayout . _defs . select ( '.patterns' )
432+ . selectAll ( '#' + fullID )
433+ . data ( [ 0 ] ) ;
434+
435+ pattern . exit ( ) . remove ( ) ;
436+
437+ pattern . enter ( )
438+ . append ( 'pattern' )
439+ . each ( function ( ) {
440+ var el = d3 . select ( this ) ;
441+
442+ el . attr ( {
443+ 'id' : fullID ,
444+ 'width' : width + 'px' ,
445+ 'height' : height + 'px' ,
446+ 'patternUnits' : 'userSpaceOnUse'
447+ } ) ;
448+
449+ var rects = el . selectAll ( 'rect' ) . data ( [ 0 ] ) ;
450+ rects . exit ( ) . remove ( ) ;
451+ if ( bgcolor ) {
452+ rects . enter ( )
453+ . append ( 'rect' )
454+ . attr ( {
455+ 'width' : width + 'px' ,
456+ 'height' : height + 'px' ,
457+ 'fill' : bgcolor
458+ } ) ;
459+ }
460+
461+ if ( shape == '.' ) {
462+ var circles = el . selectAll ( 'circle' ) . data ( [ 0 ] ) ;
463+ circles . exit ( ) . remove ( ) ;
464+ circles . enter ( )
465+ . append ( 'circle' )
466+ . attr ( {
467+ 'cx' : width / 2 ,
468+ 'cy' : height / 2 ,
469+ 'r' : solidity ,
470+ 'fill' : fgcolor
471+ } ) ;
472+ } else {
473+ var paths = el . selectAll ( 'path' ) . data ( [ 0 ] ) ;
474+ paths . exit ( ) . remove ( ) ;
475+ paths . enter ( )
476+ . append ( 'path' )
477+ . attr ( {
478+ 'd' : path ,
479+ 'stroke' : fgcolor ,
480+ 'stroke-width' : solidity + 'px'
481+ } ) ;
482+ }
483+ } ) ;
484+
485+ sel . style ( prop , getFullUrl ( fullID , gd ) )
486+ . style ( prop + '-opacity' , null ) ;
487+
488+ sel . classed ( "pattern_filled" , true ) ;
489+ var className2query = function ( s ) {
490+ return '.' + s . attr ( 'class' ) . replace ( / \s / g, '.' ) ;
491+ } ;
492+ var k = className2query ( d3 . select ( sel . node ( ) . parentNode ) ) + '>.pattern_filled' ;
493+ fullLayout . _patternUrlQueryParts [ k ] = 1 ;
494+ } ;
495+
357496/*
358497 * Make the gradients container and clear out any previous gradients.
359498 * We never collect all the gradients we need in one place,
@@ -372,6 +511,16 @@ drawing.initGradients = function(gd) {
372511 fullLayout . _gradientUrlQueryParts = { } ;
373512} ;
374513
514+ drawing . initPatterns = function ( gd ) {
515+ var fullLayout = gd . _fullLayout ;
516+
517+ var patternsGroup = Lib . ensureSingle ( fullLayout . _defs , 'g' , 'patterns' ) ;
518+ patternsGroup . selectAll ( 'pattern' ) . remove ( ) ;
519+
520+ // initialize stash of query parts filled in Drawing.pattern,
521+ // used to fix URL strings during image exports
522+ fullLayout . _patternUrlQueryParts = { } ;
523+ } ;
375524
376525drawing . pointStyle = function ( s , trace , gd ) {
377526 if ( ! s . size ( ) ) return ;
@@ -482,6 +631,16 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
482631 if ( ! gradientInfo [ gradientType ] ) gradientType = 0 ;
483632 }
484633
634+ var getPatternAttr = function ( mp , i , dflt ) {
635+ if ( mp && Array . isArray ( mp ) ) {
636+ if ( i < mp . length ) return mp [ i ] ;
637+ else return dflt ;
638+ }
639+ return mp ;
640+ } ;
641+ var markerPattern = marker . patternfill ;
642+ var patternShape = markerPattern && getPatternAttr ( markerPattern . shape , d . i , '' ) ;
643+
485644 if ( gradientType && gradientType !== 'none' ) {
486645 var gradientColor = d . mgc ;
487646 if ( gradientColor ) perPointGradient = true ;
@@ -492,6 +651,20 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
492651
493652 drawing . gradient ( sel , gd , gradientID , gradientType ,
494653 [ [ 0 , gradientColor ] , [ 1 , fillColor ] ] , 'fill' ) ;
654+ } else if ( patternShape ) {
655+ var patternBGColor = getPatternAttr ( markerPattern . bgcolor , d . i , null ) ;
656+ var patternScale = getPatternAttr ( markerPattern . scale , d . i , 1 ) ;
657+ var patternSolidity = getPatternAttr ( markerPattern . solidity , d . i , 1 ) ;
658+ var perPointPattern = Array . isArray ( markerPattern . shape ) ||
659+ Array . isArray ( markerPattern . bgcolor ) ||
660+ Array . isArray ( markerPattern . scale ) ||
661+ Array . isArray ( markerPattern . solidity ) ;
662+
663+ var patternID = trace . uid ;
664+ if ( perPointPattern ) patternID += '-' + d . i ;
665+
666+ drawing . pattern ( sel , gd , patternID , patternShape , patternBGColor , fillColor ,
667+ patternScale , patternSolidity , 'fill' ) ;
495668 } else {
496669 Color . fill ( sel , fillColor ) ;
497670 }
0 commit comments