const fabricjs = typeof fabric === "undefined" ? require ( "fabric" ) . fabric : fabric ; import PSPoint from "./PSPoint" ; export interface PSStrokeIface extends fabric . Object { type : "PSStroke" ; startTime ? : number ; endTime ? : number ; strokePoints : PSPoint [ ] ; } const min = fabricjs . util . array . min as ( arr : any [ ] , byProperty ? : string ) => number , max = fabricjs . util . array . max as ( arr : any [ ] , byProperty ? : string ) => number , extend = fabricjs . util . object . extend ; const PSStrokeImpl = < any > fabricjs . util . createClass ( fabricjs . Object , { type String default type : "PSStroke" , type Array default strokePoints : null , type number startTime : null , type number endTime : null , cacheProperties : fabricjs . Object . prototype . cacheProperties . concat ( "strokePoints" , "startTime" , "endTime" , "fillRule" ) , stateProperties : fabricjs . Object . prototype . stateProperties . concat ( "strokePoints" , "startTime" , "endTime" ) , param { Array < PSPoint > } stroke param { Object } options return { Stroke } initialize : function ( strokePoints , options ) : void { options = options || { } ; this . callSuper ( "initialize" , options ) ; this . startTime = options . startTime ; this . endTime = options . endTime ; this . strokePoints = ( strokePoints || [ ] ) . concat ( ) ; this . _setPositionDimensions ( options ) ; } , private param { Object } options _setPositionDimensions : function ( options ) : void { var calcDim = this . _parseDimensions ( ) ; this . width = calcDim . width ; this . height = calcDim . height ; if ( typeof options . left == "undefined" && typeof options . top == "undefined" ) { this . left = calcDim . left ; this . top = calcDim . top ; } this . strokeOffset = this . strokeOffset || { x : calcDim . left + this . width / 2 , y : calcDim . top + this . height / 2 } ; } , private param { CanvasRenderingContext2D } ctx _renderStroke : function ( ctx ) : void { let i , strokeWidth = this . strokeWidth / 1000 , p1 = this . strokePoints [ 0 ] , p2 = this . strokePoints [ 1 ] , mid = p1 , len = this . strokePoints . length , multSignX = 1 , multSignY = 1 , manyPoints = len > 2 , l = - this . strokeOffset . x , t = - this . strokeOffset . y ; if ( manyPoints ) { multSignX = this . strokePoints [ 2 ] . x < p2 . x ? - 1 : this . strokePoints [ 2 ] . x === p2 . x ? 0 : 1 ; multSignY = this . strokePoints [ 2 ] . y < p2 . y ? - 1 : this . strokePoints [ 2 ] . y === p2 . y ? 0 : 1 ; } if ( this . strokePoints . length === 2 && p1 . x === p2 . x && p1 . y === p2 . y ) { p1 = new PSPoint ( p1 . x , p1 . y , p1 . pressure ) ; p2 = new PSPoint ( p2 . x , p2 . y , p2 . pressure ) ; p1 . x -= strokeWidth ; p2 . x += strokeWidth ; mid . x = p1 . x ; } ctx . strokeStyle = this . stroke ; ctx . lineCap = this . strokeLineCap ; ctx . lineJoin = this . strokeLineJoin ; for ( i = 1 , len = this . strokePoints . length ; i < len ; i ++ ) { ctx . beginPath ( ) ; ctx . moveTo ( mid . x - multSignX * strokeWidth + l , mid . y - multSignY * strokeWidth + t ) ; ctx . lineWidth = p1 . pressure * this . strokeWidth ; mid = p1 . midPointFrom ( p2 ) ; ctx . quadraticCurveTo ( p1 . x - multSignX * strokeWidth + l , p1 . y - multSignY * strokeWidth + t , mid . x - multSignX * strokeWidth + l , mid . y - multSignY * strokeWidth + t ) ; p1 = this . strokePoints [ i ] ; p2 = this . strokePoints [ i + 1 ] ; ctx . stroke ( ) ; } } , private param { CanvasRenderingContext2D } ctx _render : function ( ctx ) : void { this . _renderStroke ( ctx ) ; this . _renderPaintInOrder ( ctx ) ; } , return { String } toString : function ( ) : string { return ( "#<Stroke (" + this . complexity ( ) + '): { "top": ' + this . top + ', "left": ' + this . left + " }>" ) ; } , param { Array } propertiesToInclude return { Object } toObject : function ( propertiesToInclude ) : object { var o = extend ( this . callSuper ( "toObject" , propertiesToInclude ) , { strokePoints : ( this . strokePoints as PSPoint [ ] ) . map ( i => i . clone ( ) ) , startTime : this . startTime , endTime : this . endTime , top : this . top , left : this . left } ) ; return o ; } , return { Array } _toSVG : function ( ) : string [ ] { const svgString = [ '<g transform="translate(' , String ( - this . strokeOffset . x ) , "," , String ( - this . strokeOffset . y ) , ')" ' , "COMMON_PARTS" , ">\n" ] ; let p1 = null ; for ( let i = 0 ; i < this . strokePoints . length ; i ++ ) { let p2 = this . strokePoints [ i ] ; if ( p1 ) { const x1 = p1 . x , y1 = p1 . y , x2 = p2 . x , y2 = p2 . y ; svgString . push ( "<line " , 'x1="' , String ( x1 ) , '" y1="' , String ( y1 ) , '" x2="' , String ( x2 ) , '" y2="' , String ( y2 ) , '" ' , 'stroke-width="' , String ( p1 . pressure * this . strokeWidth ) , '" ' , 'stroke-linecap="round" />\n' ) ; } p1 = p2 ; } svgString . push ( "</g>\n" ) ; return svgString ; } , param { Function } reviver return { String } toClipPathSVG : function ( reviver ) : string { return ( "\t" + this . _createBaseClipPathSVGMarkup ( this . _toSVG ( ) , { reviver : reviver } ) ) ; } , param { Function } reviver return { String } toSVG : function ( reviver ) : string { return this . _createBaseSVGMarkup ( this . _toSVG ( ) , { reviver : reviver } ) ; } , return { Number } complexity : function ( ) : number { return this . strokePoints . length ; } , private _parseDimensions : function ( ) { function DummyCtx ( ) { this . bounds = [ ] ; this . aX = [ ] ; this . aY = [ ] ; this . x = 0 ; this . y = 0 ; } DummyCtx . prototype . _done = function ( ) { this . bounds . forEach ( point => { this . aX . push ( point . x ) ; this . aY . push ( point . y ) ; } ) ; this . aX . push ( this . x ) ; this . aY . push ( this . y ) ; } ; DummyCtx . prototype . moveTo = function ( x , y ) { this . x = x ; this . y = y ; this . bounds = [ ] ; this . _done ( ) ; } ; DummyCtx . prototype . quadraticCurveTo = function ( ctlX , ctlY , x , y ) { this . bounds = fabricjs . util . getBoundsOfCurve ( this . x , this . y , ctlX , ctlY , ctlX , ctlY , x , y ) ; this . x = x ; this . y = y ; this . _done ( ) ; } ; DummyCtx . prototype . calcBounds = function ( ) : { left : Number ; top : Number ; width : Number ; height : Number ; } { var minX = min ( this . aX ) || 0 , minY = min ( this . aY ) || 0 , maxX = max ( this . aX ) || 0 , maxY = max ( this . aY ) || 0 , deltaX = maxX - minX , deltaY = maxY - minY ; return { left : minX , top : minY , width : deltaX , height : deltaY } ; } ; var ctx = new DummyCtx ( ) , i , len , p1 = this . strokePoints [ 0 ] , p2 = this . strokePoints [ 1 ] , mid = p1 ; if ( this . strokePoints . length === 2 && p1 . x === p2 . x && p1 . y === p2 . y ) { var strokeWidth = this . strokeWidth / 1000 ; p1 = new PSPoint ( p1 . x , p1 . y , p1 . pressure ) ; p2 = new PSPoint ( p2 . x , p2 . y , p2 . pressure ) ; p1 . x -= strokeWidth ; p2 . x += strokeWidth ; mid . x = p1 . x ; } for ( i = 1 , len = this . strokePoints . length ; i < len ; i ++ ) { ctx . moveTo ( mid . x , mid . y ) ; ctx . lineWidth = p1 . pressure * this . strokeWidth ; mid = p1 . midPointFrom ( p2 ) ; ctx . quadraticCurveTo ( p1 . x , p1 . y , mid . x , mid . y ) ; p1 = this . strokePoints [ i ] ; p2 = this . strokePoints [ i + 1 ] ; } return ctx . calcBounds ( ) ; } } ) ; class extends fabricjs . Object const PSStroke = PSStrokeImpl ; static memberOf param { Object } object param { Function } callback PSStroke . fromObject = function ( object , callback ) : void { ( < any > fabricjs . util ) . enlivenPatterns ( [ object . fill , object . stroke ] , function ( patterns ) { if ( typeof patterns [ 0 ] !== "undefined" ) { object . fill = patterns [ 0 ] ; } if ( typeof patterns [ 1 ] !== "undefined" ) { object . stroke = patterns [ 1 ] ; } fabricjs . util . enlivenObjects ( [ object . clipPath ] , function ( enlivedProps ) { object . clipPath = enlivedProps [ 0 ] ; fabricjs . util . enlivenObjects ( object [ "strokePoints" ] , function ( enlivendStrokePoints ) { var instance = new PSStroke ( enlivendStrokePoints , object ) ; callback && callback ( instance ) ; } , null , null ) ; } , null , null ) ; } ) ; } ; ( fabricjs as any ) . PSStroke = PSStroke ; export default PSStroke ;