@@ -354,6 +354,185 @@ drawing.gradient = function(sel, gd, gradientID, type, colorscale, prop) {
354
354
fullLayout . _gradientUrlQueryParts [ k ] = 1 ;
355
355
} ;
356
356
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 } size: size of unit squares for repetition 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 , size , solidity , prop ) {
372
+ var fullLayout = gd . _fullLayout ;
373
+ var fullID = 'p' + fullLayout . _uid + '-' + patternID ;
374
+ var width , height ;
375
+
376
+ // linear interpolation
377
+ var linearFn = function ( x , x0 , x1 , y0 , y1 ) {
378
+ return y0 + ( y1 - y0 ) * ( x - x0 ) / ( x1 - x0 ) ;
379
+ } ;
380
+
381
+ var path , linewidth , radius ;
382
+ var patternTag ;
383
+ var patternAttrs = { } ;
384
+ switch ( shape ) {
385
+ case '/' :
386
+ width = size * Math . sqrt ( 2 ) ;
387
+ height = size * Math . sqrt ( 2 ) ;
388
+ path = 'M-' + ( width / 4 ) + ',' + ( height / 4 ) + 'l' + ( width / 2 ) + ',-' + ( height / 2 ) +
389
+ 'M0,' + height + 'L' + width + ',0' +
390
+ 'M' + ( width / 4 * 3 ) + ',' + ( height / 4 * 5 ) + 'l' + ( width / 2 ) + ',-' + ( height / 2 ) ;
391
+ linewidth = solidity * size ;
392
+ patternTag = 'path' ;
393
+ patternAttrs = {
394
+ 'd' : path ,
395
+ 'stroke' : fgcolor ,
396
+ 'stroke-width' : linewidth + 'px'
397
+ } ;
398
+ break ;
399
+ case '\\' :
400
+ width = size * Math . sqrt ( 2 ) ;
401
+ height = size * Math . sqrt ( 2 ) ;
402
+ path = 'M' + ( width / 4 * 3 ) + ',-' + ( height / 4 ) + 'l' + ( width / 2 ) + ',' + ( height / 2 ) +
403
+ 'M0,0L' + width + ',' + height +
404
+ 'M-' + ( width / 4 ) + ',' + ( height / 4 * 3 ) + 'l' + ( width / 2 ) + ',' + ( height / 2 ) ;
405
+ linewidth = solidity * size ;
406
+ patternTag = 'path' ;
407
+ patternAttrs = {
408
+ 'd' : path ,
409
+ 'stroke' : fgcolor ,
410
+ 'stroke-width' : linewidth + 'px'
411
+ } ;
412
+ break ;
413
+ case 'x' :
414
+ width = size * Math . sqrt ( 2 ) ;
415
+ height = size * Math . sqrt ( 2 ) ;
416
+ path = 'M-' + ( width / 4 ) + ',' + ( height / 4 ) + 'l' + ( width / 2 ) + ',-' + ( height / 2 ) +
417
+ 'M0,' + height + 'L' + width + ',0' +
418
+ 'M' + ( width / 4 * 3 ) + ',' + ( height / 4 * 5 ) + 'l' + ( width / 2 ) + ',-' + ( height / 2 ) +
419
+ 'M' + ( width / 4 * 3 ) + ',-' + ( height / 4 ) + 'l' + ( width / 2 ) + ',' + ( height / 2 ) +
420
+ 'M0,0L' + width + ',' + height +
421
+ 'M-' + ( width / 4 ) + ',' + ( height / 4 * 3 ) + 'l' + ( width / 2 ) + ',' + ( height / 2 ) ;
422
+ linewidth = size - size * Math . sqrt ( 1.0 - solidity ) ;
423
+ patternTag = 'path' ;
424
+ patternAttrs = {
425
+ 'd' : path ,
426
+ 'stroke' : fgcolor ,
427
+ 'stroke-width' : linewidth + 'px'
428
+ } ;
429
+ break ;
430
+ case '|' :
431
+ width = size ;
432
+ height = size ;
433
+ patternTag = 'path' ;
434
+ path = 'M' + ( width / 2 ) + ',0L' + ( width / 2 ) + ',' + height ;
435
+ linewidth = solidity * size ;
436
+ patternTag = 'path' ;
437
+ patternAttrs = {
438
+ 'd' : path ,
439
+ 'stroke' : fgcolor ,
440
+ 'stroke-width' : linewidth + 'px'
441
+ } ;
442
+ break ;
443
+ case '-' :
444
+ width = size ;
445
+ height = size ;
446
+ patternTag = 'path' ;
447
+ path = 'M0,' + ( height / 2 ) + 'L' + width + ',' + ( height / 2 ) ;
448
+ linewidth = solidity * size ;
449
+ patternTag = 'path' ;
450
+ patternAttrs = {
451
+ 'd' : path ,
452
+ 'stroke' : fgcolor ,
453
+ 'stroke-width' : linewidth + 'px'
454
+ } ;
455
+ break ;
456
+ case '+' :
457
+ width = size ;
458
+ height = size ;
459
+ patternTag = 'path' ;
460
+ path = 'M' + ( width / 2 ) + ',0L' + ( width / 2 ) + ',' + height +
461
+ 'M0,' + ( height / 2 ) + 'L' + width + ',' + ( height / 2 ) ;
462
+ linewidth = size - size * Math . sqrt ( 1.0 - solidity ) ;
463
+ patternTag = 'path' ;
464
+ patternAttrs = {
465
+ 'd' : path ,
466
+ 'stroke' : fgcolor ,
467
+ 'stroke-width' : linewidth + 'px'
468
+ } ;
469
+ break ;
470
+ case '.' :
471
+ width = size ;
472
+ height = size ;
473
+ if ( solidity < Math . PI / 4 ) {
474
+ radius = Math . sqrt ( solidity * size * size / Math . PI ) ;
475
+ } else {
476
+ radius = linearFn ( solidity , Math . PI / 4 , 1.0 , size / 2 , size / Math . sqrt ( 2 ) ) ;
477
+ }
478
+ patternTag = 'circle' ;
479
+ patternAttrs = {
480
+ 'cx' : width / 2 ,
481
+ 'cy' : height / 2 ,
482
+ 'r' : radius ,
483
+ 'fill' : fgcolor
484
+ } ;
485
+ break ;
486
+ }
487
+
488
+ var pattern = fullLayout . _defs . select ( '.patterns' )
489
+ . selectAll ( '#' + fullID )
490
+ . data ( [ shape + ';' + bgcolor + ';' + fgcolor + ';' + size + ';' + solidity ] , Lib . identity ) ;
491
+
492
+ pattern . exit ( ) . remove ( ) ;
493
+
494
+ pattern . enter ( )
495
+ . append ( 'pattern' )
496
+ . each ( function ( ) {
497
+ var el = d3 . select ( this ) ;
498
+
499
+ el . attr ( {
500
+ 'id' : fullID ,
501
+ 'width' : width + 'px' ,
502
+ 'height' : height + 'px' ,
503
+ 'patternUnits' : 'userSpaceOnUse'
504
+ } ) ;
505
+
506
+ if ( bgcolor ) {
507
+ var rects = el . selectAll ( 'rect' ) . data ( [ 0 ] ) ;
508
+ rects . exit ( ) . remove ( ) ;
509
+ rects . enter ( )
510
+ . append ( 'rect' )
511
+ . attr ( {
512
+ 'width' : width + 'px' ,
513
+ 'height' : height + 'px' ,
514
+ 'fill' : bgcolor
515
+ } ) ;
516
+ }
517
+
518
+ var patterns = el . selectAll ( patternTag ) . data ( [ 0 ] ) ;
519
+ patterns . exit ( ) . remove ( ) ;
520
+ patterns . enter ( )
521
+ . append ( patternTag )
522
+ . attr ( patternAttrs ) ;
523
+ } ) ;
524
+
525
+ sel . style ( prop , getFullUrl ( fullID , gd ) )
526
+ . style ( prop + '-opacity' , null ) ;
527
+
528
+ sel . classed ( 'pattern_filled' , true ) ;
529
+ var className2query = function ( s ) {
530
+ return '.' + s . attr ( 'class' ) . replace ( / \s / g, '.' ) ;
531
+ } ;
532
+ var k = className2query ( d3 . select ( sel . node ( ) . parentNode ) ) + '>.pattern_filled' ;
533
+ fullLayout . _patternUrlQueryParts [ k ] = 1 ;
534
+ } ;
535
+
357
536
/*
358
537
* Make the gradients container and clear out any previous gradients.
359
538
* We never collect all the gradients we need in one place,
@@ -372,6 +551,23 @@ drawing.initGradients = function(gd) {
372
551
fullLayout . _gradientUrlQueryParts = { } ;
373
552
} ;
374
553
554
+ drawing . initPatterns = function ( gd ) {
555
+ var fullLayout = gd . _fullLayout ;
556
+
557
+ var patternsGroup = Lib . ensureSingle ( fullLayout . _defs , 'g' , 'patterns' ) ;
558
+ patternsGroup . selectAll ( 'pattern' ) . remove ( ) ;
559
+
560
+ // initialize stash of query parts filled in Drawing.pattern,
561
+ // used to fix URL strings during image exports
562
+ fullLayout . _patternUrlQueryParts = { } ;
563
+ } ;
564
+
565
+ drawing . getPatternAttr = function ( mp , i , dflt ) {
566
+ if ( mp && Lib . isArrayOrTypedArray ( mp ) ) {
567
+ return i < mp . length ? mp [ i ] : dflt ;
568
+ }
569
+ return mp ;
570
+ } ;
375
571
376
572
drawing . pointStyle = function ( s , trace , gd ) {
377
573
if ( ! s . size ( ) ) return ;
@@ -477,11 +673,14 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
477
673
478
674
// for legend - arrays will propagate through here, but we don't need
479
675
// to treat it as per-point.
480
- if ( Array . isArray ( gradientType ) ) {
676
+ if ( Lib . isArrayOrTypedArray ( gradientType ) ) {
481
677
gradientType = gradientType [ 0 ] ;
482
678
if ( ! gradientInfo [ gradientType ] ) gradientType = 0 ;
483
679
}
484
680
681
+ var markerPattern = marker . pattern ;
682
+ var patternShape = markerPattern && drawing . getPatternAttr ( markerPattern . shape , d . i , '' ) ;
683
+
485
684
if ( gradientType && gradientType !== 'none' ) {
486
685
var gradientColor = d . mgc ;
487
686
if ( gradientColor ) perPointGradient = true ;
@@ -492,6 +691,20 @@ drawing.singlePointStyle = function(d, sel, trace, fns, gd) {
492
691
493
692
drawing . gradient ( sel , gd , gradientID , gradientType ,
494
693
[ [ 0 , gradientColor ] , [ 1 , fillColor ] ] , 'fill' ) ;
694
+ } else if ( patternShape ) {
695
+ var patternBGColor = drawing . getPatternAttr ( markerPattern . bgcolor , d . i , null ) ;
696
+ var patternSize = drawing . getPatternAttr ( markerPattern . size , d . i , 8 ) ;
697
+ var patternSolidity = drawing . getPatternAttr ( markerPattern . solidity , d . i , 0.3 ) ;
698
+ var perPointPattern = Lib . isArrayOrTypedArray ( markerPattern . shape ) ||
699
+ Lib . isArrayOrTypedArray ( markerPattern . bgcolor ) ||
700
+ Lib . isArrayOrTypedArray ( markerPattern . size ) ||
701
+ Lib . isArrayOrTypedArray ( markerPattern . solidity ) ;
702
+
703
+ var patternID = trace . uid ;
704
+ if ( perPointPattern ) patternID += '-' + d . i ;
705
+
706
+ drawing . pattern ( sel , gd , patternID , patternShape , patternBGColor , fillColor ,
707
+ patternSize , patternSolidity , 'fill' ) ;
495
708
} else {
496
709
Color . fill ( sel , fillColor ) ;
497
710
}
0 commit comments