33var d3 = require ( '@plotly/d3' ) ;
44var isNumeric = require ( 'fast-isnumeric' ) ;
55var hasHover = require ( 'has-hover' ) ;
6+ var rough = require ( 'roughjs' ) ;
67
78var Lib = require ( '../lib' ) ;
89var nestedProperty = Lib . nestedProperty ;
@@ -385,6 +386,9 @@ function _doPlot(gd, data, layout, config) {
385386 Plots . previousPromises
386387 ) ;
387388
389+ // Implement sketchmode here (needs to happen once everything is drawn)
390+ seq . push ( sketchifyFunc ( gd ) ) ;
391+
388392 // even if everything we did was synchronous, return a promise
389393 // so that the caller doesn't care which route we took
390394 var plotDone = Lib . syncOrAsync ( seq , gd ) ;
@@ -396,6 +400,76 @@ function _doPlot(gd, data, layout, config) {
396400 } ) ;
397401}
398402
403+ function pathIsClosed ( path ) {
404+ // A closed path ends with 'Z' or 'z' (closepath)
405+ if ( ! path ) return false ;
406+ return path . trim ( ) . slice ( - 1 ) . toLowerCase ( ) === 'z' ;
407+ }
408+
409+ function extractRoughPathString ( roughPath ) {
410+ const pathElements = d3 . select ( roughPath ) . selectAll ( 'path' ) [ 0 ] ;
411+ const pathStrings = pathElements . map ( p => p . getAttribute ( 'd' ) || '' ) ;
412+ // Return concatenated pathStrings
413+ return pathStrings . join ( ' ' ) ;
414+ }
415+
416+ function sketchifyFunc ( gd ) {
417+ function sketchify ( ) {
418+ // Get value of `sketchmode` from the layout
419+ var sketchmode = gd . _fullLayout . sketchmode ;
420+ if ( ! sketchmode ) return ;
421+
422+ // Get the root svg nodes
423+ const mainSvgs = d3 . select ( gd ) . selectAll ( '.main-svg' ) [ 0 ] ;
424+
425+ for ( var mainSvg of mainSvgs ) {
426+ let roughSvg = rough . svg ( mainSvg ) ;
427+
428+ // Traverse all paths in the mainSvg and replace them with rough paths
429+ d3 . select ( mainSvg ) . selectAll ( 'path' ) . each ( function ( ) {
430+ const path = d3 . select ( this ) ;
431+ const d = path . attr ( 'd' ) ;
432+ const isClosedPath = pathIsClosed ( d ) ;
433+
434+ const pathStyle = path . attr ( 'style' ) || '' ;
435+ const pathFill = ( pathStyle . match ( / f i l l : \s * ( [ ^ ; ] * ) / ) || [ ] ) [ 1 ] ;
436+ const pathStroke = ( pathStyle . match ( / s t r o k e : \s * ( [ ^ ; ] * ) / ) || [ ] ) [ 1 ] ;
437+ const fillStyle = isClosedPath ? 'hachure' : 'solid' ;
438+
439+ if ( d ) {
440+ const options = {
441+ stroke : ( pathStroke !== 'none' ) ? pathStroke : pathFill ,
442+ fill : ( pathFill !== 'none' ) ? pathFill : pathStroke ,
443+ fillStyle : fillStyle ,
444+ fillWeight : 3 ,
445+ hachureAngle : - 45 ,
446+ hachureGap : 6 ,
447+ }
448+
449+ const roughPath = roughSvg . path ( d , options ) ;
450+
451+ const roughPathString = extractRoughPathString ( roughPath ) ;
452+
453+ if ( ! roughPathString ) return ;
454+
455+ // replace 'd' attribute of original path with that of rough path
456+ const strokeWidth = 2 ;
457+ path . attr ( 'd' , roughPathString ) ;
458+
459+ path . attr ( 'style' , undefined ) ;
460+ path . attr ( 'stroke-width' , '1' )
461+ path . attr ( 'fill' , 'none' ) ;
462+ if ( pathFill || pathStroke ) {
463+ const strokeToSet = ( pathStroke && pathStroke !== 'none' ) ? pathStroke : pathFill ;
464+ path . attr ( 'stroke' , strokeToSet ) ;
465+ }
466+ }
467+ } ) ;
468+ }
469+ }
470+ return sketchify ;
471+ }
472+
399473function emitAfterPlot ( gd ) {
400474 var fullLayout = gd . _fullLayout ;
401475
@@ -1768,6 +1842,8 @@ function relayout(gd, astr, val) {
17681842 relayout , [ gd , specs . redoit ]
17691843 ) ;
17701844
1845+ seq . push ( sketchifyFunc ( gd ) ) ;
1846+
17711847 var plotDone = Lib . syncOrAsync ( seq , gd ) ;
17721848 if ( ! plotDone || ! plotDone . then ) plotDone = Promise . resolve ( gd ) ;
17731849
@@ -2288,6 +2364,8 @@ function update(gd, traceUpdate, layoutUpdate, _traces) {
22882364 Plots . reselect
22892365 ) ;
22902366
2367+ seq . push ( sketchifyFunc ( gd ) ) ;
2368+
22912369 Queue . add ( gd ,
22922370 update , [ gd , restyleSpecs . undoit , relayoutSpecs . undoit , restyleSpecs . traces ] ,
22932371 update , [ gd , restyleSpecs . redoit , relayoutSpecs . redoit , restyleSpecs . traces ]
0 commit comments