@@ -17,6 +17,49 @@ import diffGrid from './utils/diffGrid';
1717
1818type Pixel = { x : number , y : number } ;
1919
20+ enum HistoryType {
21+ Group ,
22+ Pixel ,
23+ ColorUpdate ,
24+ ColorAdd ,
25+ ColorRemove ,
26+ LayerAdd ,
27+ LayerRemove ,
28+ LayerName ,
29+ LayerLock ,
30+ LayerUnlock ,
31+ LayerExport ,
32+ LayerVisible ,
33+ LayerOpacity
34+ }
35+
36+ type HistoryGroupType = {
37+ name : string
38+ }
39+
40+ type HistoryPixelType = {
41+ pixels : [ number , number , number ] [ ] ,
42+ layer : number
43+ }
44+
45+ type HistoryColorUpdateType = {
46+ color : [ number , number , number , number ] ,
47+ index : number
48+ }
49+
50+ type History = {
51+ type : HistoryType ,
52+ data : HistoryGroupType | HistoryPixelType | HistoryColorUpdateType
53+ }
54+
55+ type Layer = {
56+ name : string ,
57+ visible : boolean ,
58+ locked : boolean ,
59+ opacity : number ,
60+ export : boolean
61+ }
62+
2063@Component ( {
2164 selector : 'pg-input-pixel-editor' ,
2265 style,
@@ -27,7 +70,6 @@ export default class PgInputPixelEditor extends HTMLElement {
2770 @Prop ( normalizeInt ) height : number = 10 ;
2871 @Prop ( normalizeInt ) size : number = 10 ;
2972 @Prop ( normalizeInt ) gridSize : number = 1 ;
30- @Prop ( ) value : string = '' ;
3173 @Prop ( ) placeholder : string = '' ;
3274
3375 @Part ( ) $canvas : HTMLCanvasElement ;
@@ -41,12 +83,14 @@ export default class PgInputPixelEditor extends HTMLElement {
4183 #startY: number = - 1 ;
4284 #x: number = - 1 ;
4385 #y: number = - 1 ;
86+ #layer: number = 0 ;
87+ #layers: Layer [ ] = [ ] ;
4488 #isCtrl: boolean = false ;
4589 #isShift: boolean = false ;
4690 #isAlt: boolean = false ;
47- #data: number [ ] [ ] = [ ] ;
48- #undoHistory: [ number , number , number ] [ ] [ ] = [ ] ;
49- #redoHistory: [ number , number , number ] [ ] [ ] = [ ] ;
91+ #data: number [ ] [ ] [ ] = [ ] ;
92+ #undoHistory: History [ ] = [ ] ;
93+ #redoHistory: History [ ] = [ ] ;
5094 #context: CanvasRenderingContext2D ;
5195 #colors: string [ ] = [ 'transparent' , '#000' ] ;
5296 #baseLayer: HTMLCanvasElement ;
@@ -114,15 +158,23 @@ export default class PgInputPixelEditor extends HTMLElement {
114158 const actualHeight = this . height * totalSize - this . gridSize ;
115159 this . $canvas . width = actualWidth ;
116160 this . $canvas . height = actualHeight ;
117- this . #data = fillGrid ( this . width , this . height ) ;
161+ this . #layer = 0 ;
162+ this . #layers = [ {
163+ name : 'Layer 1' ,
164+ export : true ,
165+ locked : false ,
166+ visible : true ,
167+ opacity : 1
168+ } ] ;
169+ this . #data = [ fillGrid ( this . width , this . height ) ] ;
118170 [ this . #baseLayer, this . #baseLayerContext] = createLayer ( actualWidth , actualHeight ) ;
119171 [ this . #editLayer, this . #editLayerContext] = createLayer ( actualWidth , actualHeight ) ;
120172 [ this . #noEditLayer, this . #noEditLayerContext] = createLayer ( actualWidth , actualHeight ) ;
121173 [ this . #previewLayer, this . #previewLayerContext] = createLayer ( actualWidth , actualHeight ) ;
122174 }
123175
124176 #handleChange( ) {
125- const path = bitmaskToPath ( this . #data, { scale : 1 } ) ;
177+ const path = bitmaskToPath ( this . #data[ this . #layer ] , { scale : 1 } ) ;
126178 console . log ( 'change:' , path ) ;
127179 this . dispatchEvent ( new CustomEvent ( 'change' , {
128180 detail : { value : path }
@@ -138,7 +190,7 @@ export default class PgInputPixelEditor extends HTMLElement {
138190 this . #delayTimerId = window . setTimeout ( this . #handleChange. bind ( this ) , 1000 ) ;
139191 } ;
140192
141- #setPixel ( x : number , y : number , color : number ) {
193+ #setPixel( x : number , y : number , color : number ) {
142194 if ( x > this . width ) {
143195 throw new Error ( `Invalid x; ${ x } > ${ this . width } ` ) ;
144196 }
@@ -179,9 +231,9 @@ export default class PgInputPixelEditor extends HTMLElement {
179231 x * totalSize , y * totalSize , this . size + 2 , this . size + 2 ,
180232 x * totalSize , y * totalSize , this . size + 2 , this . size + 2
181233 ) ;
182- console . log ( 'draw pixel(x, y, color, data):' , x , y , color , this . #data[ y ] [ x ] ) ;
234+ console . log ( 'draw pixel(x, y, color, data):' , x , y , color , this . #data[ this . #layer ] [ y ] [ x ] ) ;
183235 // Verify this is the only place setting pixel data!
184- this . #data[ y ] [ x ] = color ;
236+ this . #data[ this . #layer ] [ y ] [ x ] = color ;
185237 this . #delayedChange( ) ;
186238 }
187239
@@ -289,7 +341,7 @@ export default class PgInputPixelEditor extends HTMLElement {
289341 if ( newX >= this . width ) { newX = this . width - 1 ; }
290342 if ( newY >= this . height ) { newY = this . height - 1 ; }
291343 this . #isPressed = true ;
292- this . #startColor = this . #data[ newY ] [ newX ] ;
344+ this . #startColor = this . #data[ this . #layer ] [ newY ] [ newX ] ;
293345 this . #startX = newX ;
294346 this . #startY = newY ;
295347 this . #x = newX ;
@@ -299,7 +351,7 @@ export default class PgInputPixelEditor extends HTMLElement {
299351 switch ( this . #inputMode) {
300352 case InputMode . Pixel :
301353 this . #setPixel( newX , newY , color ) ;
302- this . #data[ newY ] [ newX ] = color ;
354+ this . #data[ this . #layer ] [ newY ] [ newX ] = color ;
303355 break ;
304356 }
305357 console . log ( this . #inputMode, newX , newY ) ;
@@ -320,7 +372,7 @@ export default class PgInputPixelEditor extends HTMLElement {
320372 switch ( this . #inputMode) {
321373 case InputMode . Pixel :
322374 this . #setPixel( newX , newY , 0 ) ;
323- this . #data[ newY ] [ newX ] = 0 ;
375+ this . #data[ this . #layer ] [ newY ] [ newX ] = 0 ;
324376 break ;
325377 }
326378 } else {
@@ -405,7 +457,7 @@ export default class PgInputPixelEditor extends HTMLElement {
405457 case InputMode . Pixel :
406458 for ( var point of points ) {
407459 this . #setPixel( point [ 0 ] , point [ 1 ] , color ) ;
408- data [ point [ 1 ] ] [ point [ 0 ] ] = color ;
460+ data [ this . #layer ] [ point [ 1 ] ] [ point [ 0 ] ] = color ;
409461 }
410462 break ;
411463 case InputMode . Line :
@@ -460,99 +512,118 @@ export default class PgInputPixelEditor extends HTMLElement {
460512 // ToDo: Code this
461513 }
462514 clear ( ) {
463- this . #data = fillGrid ( this . width , this . height ) ;
515+ const gridEmpty = fillGrid ( this . width , this . height ) ;
516+ const diff = diffGrid ( this . #data[ this . #layer] , gridEmpty ) ;
517+ this . #undoHistory. push ( {
518+ type : HistoryType . Group ,
519+ data : {
520+ name : 'Clear'
521+ }
522+ } ) ;
523+ this . #undoHistory. push ( {
524+ type : HistoryType . Pixel ,
525+ data : {
526+ pixels : [ ] ,
527+ layer : this . #layer
528+ }
529+ } ) ;
530+ this . #data = [ fillGrid ( this . width , this . height ) ] ;
464531 this . #updateGrid( ) ;
465532 }
466533 clearHistory ( ) {
467534 this . #undoHistory = [ ] ;
468535 this . #redoHistory = [ ] ;
469536 }
470537 applyTemplate ( template : number [ ] [ ] ) {
471- this . #data = template ;
538+ this . #data = [ template ] ;
472539 }
473540 flipHorizontal ( ) {
474- const cloned = cloneGrid ( this . #data) ;
541+ const cloned = cloneGrid ( this . #data[ this . #layer ] ) ;
475542 const w = cloned [ 0 ] . length - 1 ;
476- iterateGrid ( this . #data, ( x , y ) => {
477- cloned [ y ] [ x ] = this . #data[ y ] [ w - x ] ;
543+ iterateGrid ( this . #data[ this . #layer ] , ( x , y ) => {
544+ cloned [ y ] [ x ] = this . #data[ this . #layer ] [ y ] [ w - x ] ;
478545 } ) ;
479- this . #data = cloned ;
546+ this . #data[ this . #layer ] = cloned ;
480547 }
481548 flipVertical ( ) {
482- const cloned = cloneGrid ( this . #data) ;
549+ const cloned = cloneGrid ( this . #data[ this . #layer ] ) ;
483550 const h = cloned . length - 1 ;
484- iterateGrid ( this . #data, ( x , y ) => {
485- cloned [ y ] [ x ] = this . #data[ h - y ] [ x ] ;
551+ iterateGrid ( this . #data[ this . #layer ] , ( x , y ) => {
552+ cloned [ this . #layer ] [ y ] [ x ] = this . #data[ h - y ] [ x ] ;
486553 } ) ;
487- this . #data = cloned ;
554+ this . #data[ this . #layer ] = cloned ;
488555 }
489556 move ( translateX : number , translateY : number ) {
490557 const cloned = fillGrid ( this . width , this . height ) ;
491558 for ( let iy = 0 ; iy < this . height ; iy ++ ) {
492559 cloned [ iy ] . fill ( 0 ) ;
493560 }
494- iterateGrid ( this . #data, ( x , y ) => {
561+ iterateGrid ( this . #data[ this . #layer ] , ( x , y ) => {
495562 if ( y - translateY < 0
496563 || x - translateX < 0
497564 || y - translateY >= this . height
498565 || x - translateX >= this . width ) {
499566 return ;
500567 }
501- cloned [ y ] [ x ] = this . #data[ y - translateY ] [ x - translateX ] ;
568+ cloned [ y ] [ x ] = this . #data[ this . #layer ] [ y - translateY ] [ x - translateX ] ;
502569 } ) ;
503- this . #data = cloned ;
570+ this . #data[ this . #layer ] = cloned ;
504571 }
505572 invert ( ) {
506573 // Only works with 2 colors
507574 if ( this . #colors. length > 2 ) {
508575 return ;
509576 }
510- const cloned = cloneGrid ( this . #data) ;
577+ const cloned = cloneGrid ( this . #data[ this . #layer ] ) ;
511578 iterateGrid ( cloned , ( x , y ) => {
512579 cloned [ y ] [ x ] = cloned [ y ] [ x ] === 0 ? 1 : 0 ;
513580 } ) ;
514- this . #data = cloned ;
581+ this . #data[ this . #layer ] = cloned ;
515582 }
516583 undo ( ) {
517584 // ToDo: Rewrite to use new history api
518585 const revert = this . #undoHistory. pop ( ) ;
519586 if ( ! revert ) { return ; }
520- this . #redoHistory. push ( revert ) ;
521- revert ?. forEach ( ( item ) => {
522- const [ x , y ] = item ;
523- this . #data[ y ] [ x ] = item [ 2 ] ;
524- // redraw canvas
525- } ) ;
587+ switch ( revert . type ) {
588+ case HistoryType . Pixel :
589+ this . #redoHistory. push ( revert ) ;
590+ ( revert . data as HistoryPixelType ) . pixels . forEach ( ( item ) => {
591+ const [ x , y ] = item ;
592+ this . #data[ this . #layer] [ y ] [ x ] = item [ 2 ] ;
593+ // redraw canvas
594+ } ) ;
595+ break ;
596+ }
526597 }
527598 redo ( ) {
528599 // ToDo: Rewrite to use new history api
529- const revert = this . #redoHistory. pop ( ) ;
600+ /* const revert = this.#redoHistory.pop();
530601 if (!revert) { return; }
531602 this.#undoHistory.push(revert);
532603 revert?.forEach((item) => {
533604 const [x, y] = item;
534605 this.#data[y][x] = item[2];
535606 // redraw canvas
536- } ) ;
607+ });*/
537608 }
538609 rotate ( counterClockwise : boolean = false ) {
539- const cloned = cloneGrid ( this . #data) ;
610+ const cloned = cloneGrid ( this . #data[ this . #layer ] ) ;
540611 if ( counterClockwise ) {
541612 const newData = this . #data[ 0 ] . map ( ( val , index ) => this . #data. map ( row => row [ row . length - 1 - index ] ) ) ;
542613 for ( let iy = 0 ; iy < this . height ; iy ++ ) {
543614 for ( let ix = 0 ; ix < this . width ; ix ++ ) {
544- cloned [ iy ] [ ix ] = newData [ iy ] [ ix ] ;
615+ cloned [ iy ] [ ix ] = newData [ this . #layer ] [ iy ] [ ix ] ;
545616 }
546617 }
547618 } else {
548619 const newData = this . #data[ 0 ] . map ( ( val , index ) => this . #data. map ( row => row [ index ] ) . reverse ( ) ) ;
549620 for ( let iy = 0 ; iy < this . height ; iy ++ ) {
550621 for ( let ix = 0 ; ix < this . width ; ix ++ ) {
551- cloned [ iy ] [ ix ] = newData [ iy ] [ ix ] ;
622+ cloned [ iy ] [ ix ] = newData [ this . #layer ] [ iy ] [ ix ] ;
552623 }
553624 }
554625 }
555- this . #data = cloned ;
626+ this . #data[ this . #layer ] = cloned ;
556627 }
557628 hasUndo ( ) {
558629 return this . #undoHistory. length !== 0 ;
0 commit comments