import type { Fn0 , IGrid2D , NumOrString } from "@thi.ng/api" ; import { IGrid2DMixin } from "@thi.ng/api/mixins/igrid" ; import { peek } from "@thi.ng/arrays/peek" ; import { clamp } from "@thi.ng/math/interval" ; import { NONE } from "@thi.ng/text-format/api" ; import { ClipRect , StrokeStyle , STYLE_ASCII } from "./api.js" ; import { charCode , intersectRect } from "./utils.js" ; @ IGrid2DMixin export class Canvas implements IGrid2D < Uint32Array , number > { data : Uint32Array ; size : [ number , number ] ; stride : [ number , number ] ; format : number ; defaultFormat : number ; styles : StrokeStyle [ ] ; clipRects : ClipRect [ ] ; constructor ( width , height , format = NONE , style = STYLE_ASCII ) { this . size = [ width , height ] ; this . stride = [ 1 , this . width ] ; this . format = this . defaultFormat = format ; this . data = new Uint32Array ( width * height ) . fill ( charCode ( 0x20 , format ) ) ; this . styles = [ style ] ; this . clipRects = [ { x1 : 0 , y1 : 0 , x2 : width , y2 : height , w : width , h : height } , ] ; } get width ( ) { return this . size [ 0 ] ; } get height ( ) { return this . size [ 1 ] ; } get offset ( ) { return 0 ; } get dim ( ) : 2 { return 2 ; } order ( ) { } includes ( d0 , d1 ) { } indexAt ( d0 , d1 ) { } indexAtUnsafe ( d0 , d1 ) { } getAt ( x , y ) { } getAtUnsafe ( x , y ) { } setAt ( x , y , col ) { } setAtUnsafe ( x , y , col ) { } } export const canvas = ( width , height , format ? , style ? ) => new Canvas ( width , height , format , style ) ; export const beginClip = ( canvas , x , y , w , h ) => { x |= 0 ; y |= 0 ; w |= 0 ; h |= 0 ; const { width , height } = canvas ; const x2 = clamp ( x + w , 0 , width ) ; const y2 = clamp ( y + h , 0 , height ) ; const x1 = clamp ( x , 0 , width ) ; const y1 = clamp ( y , 0 , height ) ; canvas . clipRects . push ( intersectRect ( { x1 , y1 , x2 , y2 , w : x2 - x1 , h : y2 - y1 } , peek ( canvas . clipRects ) ) ) ; } ; const pop = ( stack ) => stack . length > 1 && stack . pop ( ) ; export const endClip = ( canvas ) => pop ( canvas . clipRects ) ; export const withClip = ( canvas , x , y , w , h , fn ) => { beginClip ( canvas , x , y , w , h ) ; fn ( ) ; canvas . clipRects . pop ( ) ; } ; export const beginStyle = ( canvas , style ) => { canvas . styles . push ( style ) ; } ; export const endStyle = ( canvas ) => pop ( canvas . styles ) ; export const withStyle = ( canvas , style , fn ) => { canvas . styles . push ( style ) ; fn ( ) ; canvas . styles . pop ( ) ; } ; export const setFormat = ( canvas , format ) => ( canvas . format = format ) ; export const withFormat = ( canvas , format , fn ) => { const prev = canvas . format ; canvas . format = format ; fn ( ) ; canvas . format = prev ; } ; export const getAt = ( canvas , x , y ) => { x |= 0 ; y |= 0 ; return x >= 0 && y >= 0 && x < canvas . width && y < canvas . height ? canvas . data [ x + y * canvas . width ] : 0 ; } ; export const setAt = ( canvas , x , y , code , format = canvas . format ) => { x |= 0 ; y |= 0 ; const { x1 , y1 , x2 , y2 } = peek ( canvas . clipRects ) ; if ( x < x1 || y < y1 || x >= x2 || y >= y2 ) return ; canvas . data [ x + y * canvas . width ] = charCode ( code , format ) ; } ;