import { BackgroundTexture } from './BackgroundTexture' ; import { OptionManager } from './OptionManager' ; import { BoardManager } from './boardManager' ; import { Layout } from './Layout' ; import { ActionFreeDraw } from './ActionFreeDraw' ; import { getCanvas } from './main' ; import { UserManager } from './UserManager' ; /**
 * general drawing class with low level functions for drawing on the canvas
 */ export class Drawing { private static patterns = { } ; static createChalkPatternCanvas ( color ) { const patternCanvas = document . createElement ( 'canvas' ) ; const context = patternCanvas . getContext ( '2d' ) ; const size = 128 ; patternCanvas . width = size ; patternCanvas . height = size ; const lineWidth = 2 ; context . fillStyle = color ; for ( let i = 0 ; i < size * size / 20 ; i ++ ) { const x = size * Math . random ( ) ; const y = size * Math . random ( ) ; const shiftForChalkEffectDrawing = 0.65 ; context . globalAlpha = Math . random ( ) * 0.5 ; context . fillRect ( x + ( - lineWidth + Math . random ( ) - shiftForChalkEffectDrawing ) * context . lineWidth , y + ( - lineWidth + Math . random ( ) - shiftForChalkEffectDrawing ) * context . lineWidth , 2 * lineWidth * shiftForChalkEffectDrawing * ( 1 + Math . random ( ) ) , 2 * lineWidth * shiftForChalkEffectDrawing * ( 1 + Math . random ( ) ) ) ; const shiftForChalkEffectClear = 0.35 ; context . clearRect ( x + ( - lineWidth + Math . random ( ) - shiftForChalkEffectClear ) * context . lineWidth , y + ( - lineWidth + Math . random ( ) - shiftForChalkEffectClear ) * context . lineWidth , 2 * lineWidth * shiftForChalkEffectClear * ( 1 + Math . random ( ) ) , 2 * lineWidth * shiftForChalkEffectClear * ( 1 + Math . random ( ) ) ) ; } return patternCanvas ; } static getChalkPatternCanvas ( color ) { if ( Drawing . patterns [ color ] == undefined ) Drawing . patterns [ color ] = Drawing . createChalkPatternCanvas ( color ) ; return Drawing . patterns [ color ] ; } static fill ( ctx , points , color ) { ctx . beginPath ( ) ; ctx . moveTo ( points [ 0 ] . x , points [ 0 ] . y ) ; for ( let i = 1 ; i < points . length ; i ++ ) ctx . lineTo ( points [ i ] . x , points [ i ] . y ) ; ctx . closePath ( ) ; if ( Drawing . isChalkEffect ) ctx . fillStyle = ctx . createPattern ( Drawing . getChalkPatternCanvas ( color ) , "repeat" ) ; else { ctx . globalAlpha = 0.5 ; ctx . fillStyle = color ; } ctx . fill ( ) ; } static lineWidth : number ; private static isChalkEffect = false ; private static isEraserEffect = false ; static init ( ) { OptionManager . number ( { name : "lineWidth" , defaultValue : 1.5 , onChange : ( lineWidth ) => { this . lineWidth = lineWidth ; Drawing . clear ( ) ; BoardManager . timeline . canvasRedraw ( ) ; } } ) ; OptionManager . boolean ( { name : "chalkEffect" , defaultValue : false , onChange : ( s ) => { Drawing . isChalkEffect = s ; Drawing . clear ( ) ; BoardManager . timeline . canvasRedraw ( ) ; } } ) ; OptionManager . boolean ( { name : "eraserEffect" , defaultValue : false , onChange : ( s ) => { Drawing . isEraserEffect = s ; Drawing . clear ( ) ; BoardManager . timeline . canvasRedraw ( ) ; } } ) ; } param points description static clearPolygon ( points ) { const canvas = getCanvas ( ) ; const context = canvas . getContext ( "2d" ) ; context . save ( ) ; context . beginPath ( ) ; context . moveTo ( points [ 0 ] . x , points [ 0 ] . y ) ; for ( const point of points ) { context . lineTo ( point . x , point . y ) ; } context . clip ( ) ; context . clearRect ( 0 , 0 , canvas . width , canvas . height ) ; context . restore ( ) ; context . globalCompositeOperation = "source-over" ; } param points description static removeContour ( points ) { const canvas = getCanvas ( ) ; const context = canvas . getContext ( "2d" ) ; context . globalCompositeOperation = "destination-out" ; context . strokeStyle = "rgba(255, 255, 255, 1)" ; context . lineWidth = 6 ; context . globalAlpha = 1.0 ; context . moveTo ( points [ 0 ] . x , points [ 0 ] . y ) ; for ( const point of points ) { context . lineTo ( point . x , point . y ) ; } context . stroke ( ) ; } static drawLine ( context , x1 , y1 , x2 , y2 , pressure = 1.0 , color = UserManager . me . getCurrentColor ( ) ) { context . beginPath ( ) ; context . strokeStyle = color ; context . globalCompositeOperation = "source-over" ; context . globalAlpha = 0.9 + 0.1 * pressure ; context . lineWidth = this . lineWidth * ( 1 + 2 * pressure ) ; context . moveTo ( x1 , y1 ) ; context . lineTo ( x2 , y2 ) ; context . stroke ( ) ; /****** Chalk effect (too slow in real time!) ***/ if ( Drawing . isChalkEffect ) { context . fillStyle = color ; context . globalAlpha = 1 ; const dist = Math . round ( Math . sqrt ( ( x2 - x1 ) ** 2 + ( y2 - y1 ) ** 2 ) ) ; for ( let i = 0 ; i < dist ; i += 8 ) { const x = x1 + ( i * ( x2 - x1 ) / dist ) ; const y = y1 + ( i * ( y2 - y1 ) / dist ) ; const shiftForChalkEffectClear = 0.35 ; context . clearRect ( x + ( Math . random ( ) - shiftForChalkEffectClear ) * context . lineWidth , y + ( Math . random ( ) - shiftForChalkEffectClear ) * context . lineWidth , 2 * shiftForChalkEffectClear * ( 1 + Math . random ( ) ) , 2 * shiftForChalkEffectClear * ( 1 + Math . random ( ) ) ) ; const shiftForChalkEffectDrawing = 0.35 ; context . fillRect ( x + ( Math . random ( ) - shiftForChalkEffectDrawing ) * context . lineWidth , y + ( Math . random ( ) - shiftForChalkEffectDrawing ) * context . lineWidth , 2 * shiftForChalkEffectDrawing * ( 1 + Math . random ( ) ) , 2 * shiftForChalkEffectDrawing * ( 1 + Math . random ( ) ) ) ; } } } description static clear ( ) { const canvas = getCanvas ( ) ; const context = canvas . getContext ( "2d" ) ; context . clearRect ( 0 , 0 , canvas . width , canvas . height ) ; } static drawLineUndo ( context , x1 , y1 , x2 , y2 , pressure = 1.0 ) { context . beginPath ( ) ; context . strokeStyle = "rgba(255,255,255,1)" ; context . globalCompositeOperation = "source-over" ; context . globalAlpha = 0.9 + 0.1 * pressure ; context . lineWidth = this . lineWidth * ( 1 + 2 * pressure ) ; context . moveTo ( x1 , y1 ) ; context . lineTo ( x2 , y2 ) ; context . stroke ( ) ; context . closePath ( ) ; } static drawDot ( context , x , y , color ) { context . beginPath ( ) ; context . fillStyle = color ; context . lineWidth = this . lineWidth * 2 ; context . arc ( x , y , 2 , 0 , 2 * Math . PI ) ; context . fill ( ) ; } static clearLine ( x1 , y1 , x2 , y2 , lineWidth = 10 ) { const context = getCanvas ( ) . getContext ( "2d" ) ; let r = 0 ; let g = 0 ; let b = 0 ; let a = 0 ; const lineWidth2 = Math . floor ( lineWidth / 2 ) ; if ( Drawing . isEraserEffect ) { let n = 0 ; const pixels = context . getImageData ( x1 - lineWidth2 , y1 - lineWidth2 , lineWidth , lineWidth ) . data ; const nbPixels = 6 ; let maxAlpha = 0 ; for ( let j = 0 ; j < nbPixels ; j ++ ) { const i = Math . floor ( Math . random ( ) * pixels . length / 4 ) ; if ( pixels [ 4 * i + 3 ] > 0 ) { n += pixels [ 4 * i + 3 ] ; r += pixels [ 4 * i ] ; g += pixels [ 4 * i + 1 ] ; b += pixels [ 4 * i + 2 ] ; maxAlpha = Math . max ( pixels [ 4 * i + 3 ] , maxAlpha ) ; } } if ( n > 0 ) { const seuil = ( x ) => ( Math . min ( 255 , x / nbPixels ) ) ; r = seuil ( r ) ; g = seuil ( g ) ; b = seuil ( b ) ; a = Math . min ( 0.5 , maxAlpha * 0.7 / 255 , 255 * n / ( nbPixels * 255 ) ) ; } } context . beginPath ( ) ; context . globalCompositeOperation = "destination-out" ; context . strokeStyle = `rgba(${ 255 },${ 255 },${ 255 },${ 0.9 })` ; context . lineWidth = lineWidth ; context . moveTo ( x1 , y1 ) ; context . lineTo ( x2 , y2 ) ; context . lineCap = "round" ; context . stroke ( ) ; if ( Drawing . isEraserEffect ) { context . beginPath ( ) ; context . globalCompositeOperation = "source-over" ; context . strokeStyle = `rgba(${ r },${ g },${ b },${ a })` ; context . lineWidth = lineWidth ; context . moveTo ( x1 , y1 ) ; context . lineTo ( x2 , y2 ) ; context . lineCap = "round" ; context . stroke ( ) ; } } static drawRectangle ( { x1 , y1 , x2 , y2 } , color ) { const context = getCanvas ( ) . getContext ( "2d" ) ; context . beginPath ( ) ; context . strokeStyle = color ; context . globalCompositeOperation = "source-over" ; context . globalAlpha = 1 ; context . lineWidth = this . lineWidth ; context . rect ( x1 , y1 , x2 - x1 , y2 - y1 ) ; context . stroke ( ) ; } static drawEllipse ( { cx , cy , rx , ry } , color ) { const context = getCanvas ( ) . getContext ( "2d" ) ; context . beginPath ( ) ; context . strokeStyle = color ; context . globalCompositeOperation = "source-over" ; context . globalAlpha = 1 ; context . lineWidth = this . lineWidth ; context . ellipse ( cx , cy , rx , ry , 0 , 0 , 2 * Math . PI , false ) ; context . stroke ( ) ; } static divideScreen ( userid , x ) { console . log ( "divide the screen" ) ; const action = new ActionFreeDraw ( userid ) ; action . addPoint ( { x : x , y : 0 , pressure : 1 , color : BackgroundTexture . getDefaultChalkColor ( ) } ) ; action . addPoint ( { x : x , y : Layout . getWindowHeight ( ) , pressure : 1 , color : BackgroundTexture . getDefaultChalkColor ( ) } ) ; action . redo ( ) ; BoardManager . addAction ( action ) ; } }