import { assert } from './auxiliaries' ; import { GLsizei3 } from './tuples' ; export interface KernelJSON { kernel : any ; size : { width : number ; height : number ; depth : number ; } ; } /**
 * Kernel class is based on the idea of glkernel  https and is the basis for
 * various auxiliary kernels that are mainly intended for but not limited to GPU use. A kernel stores samples in a
 * three-dimensional structure. Each sample can have up to four components (vec4) and is strongly typed, e.g.,
 *  KernelF32 . The samples can be accessed and modified in various ways and easily passed to the GPU.
  export abstract class AbstractKernel < T extends Float32Array | Uint32Array | Int32Array | Uint8Array | Int8Array > { protected _samples : T ; see width protected _width : GLsizei ; see height protected _height : GLsizei ; see depth protected _depth : GLsizei ; see components protected _components : 1 | 2 | 3 | 4 ; constructor ( components , width , height = 1 , depth = 1 ) { this . _components = components ; this . _width = isNaN ( width ) ? 1 : Math . max ( 1 , width ) ; this . _height = Math . max ( 1 , height ) ; this . _depth = Math . max ( 1 , depth ) ; this . resize ( ) ; } /**
     * Should resize the samples array w.r.t. to the typed array buffer.
     * The resulting array buffer may be empty.
     */ protected abstract resize ( ) ; param xPosOrIndex param yPos param zPos returns get ( xPosOrIndex , yPos ? , zPos ? ) { let i ; if ( yPos === undefined && zPos === undefined ) { i = this . _components * xPosOrIndex ; } else { i = this . index ( xPosOrIndex , yPos , zPos ? zPos : 0 ) ; } assert ( i < this . length , `index expected to be in range [0, ${ this . length - 1 }], given ${ i }` ) ; switch ( this . _components ) { case 1 : return [ this . _samples [ i ] ] ; case 2 : return [ this . _samples [ i ] , this . _samples [ i + 1 ] ] ; case 3 : return [ this . _samples [ i ] , this . _samples [ i + 1 ] , this . _samples [ i + 2 ] ] ; case 4 : return [ this . _samples [ i ] , this . _samples [ i + 1 ] , this . _samples [ i + 2 ] , this . _samples [ i + 3 ] ] ; } } param sample param xPosOrIndex param yPos param zPos set ( sample , xPosOrIndex , yPos ? , zPos ? ) { assert ( sample . length === this . _components , `number of components expected to be ${ this . _components }, given ${ sample . length }` ) ; let i ; if ( yPos === undefined && zPos === undefined ) { i = this . _components * Math . min ( this . elements , Math . max ( 0 , xPosOrIndex ) ) ; } else { i = this . _components * this . index ( xPosOrIndex , yPos , zPos ? zPos : 0 ) ; } assert ( i < this . length , `index expected to be in range [0, ${ this . length - 1 }], given ${ i }` ) ; switch ( this . _components ) { case 4 : this . _samples [ i + 3 ] = sample [ 3 ] ; case 3 : this . _samples [ i + 2 ] = sample [ 2 ] ; case 2 : this . _samples [ i + 1 ] = sample [ 1 ] ; case 1 : this . _samples [ i + 0 ] = sample [ 0 ] ; break ; } } param samples abstract fromArray ( samples ) ; param json - JSON object either from file, or set manually. These kernels can be generated using, e.g.,
     * glkernel  https .
      fromJSON ( json ) { assert ( json . size && json . size . width !== undefined && json . size . height !== undefined && json . size . depth !== undefined , `expected kernel width, height, and depth to be set, given '${ json . size }'` ) ; const flatten = ( array ) : Array < number > => array . reduce ( ( a , b ) => a . concat ( Array . isArray ( b ) ? flatten ( b ) : b ) , [ ] ) ; this . _width = Math . max ( 1 , json . size . width ) ; this . _height = Math . max ( 1 , json . size . height ) ; this . _depth = Math . max ( 1 , json . size . depth ) ; const array = flatten ( json . kernel ) ; this . _components = Math . floor ( array . length / this . elements ) as 1 | 2 | 3 | 4 ; this . resize ( ) ; this . fromArray ( array ) ; } param xPos param yPos param zPos returns index ( xPos , yPos = 0 , zPos = 0 ) { return this . _width * ( this . _height * Math . min ( this . _depth , Math . max ( 0 , zPos ) ) + Math . min ( this . _height , Math . max ( 0 , yPos ) ) ) + Math . min ( this . _width , Math . max ( 0 , xPos ) ) ; } param index returns position ( index ) { let clamped = Math . min ( this . elements , Math . max ( 0 , index ) ) ; const zPos = Math . floor ( clamped / ( this . _width * this . _height ) ) ; clamped -= zPos * this . _width * this . _height ; const yPos = Math . floor ( clamped / this . _width ) ; clamped -= yPos * this . _width ; const xPos = clamped ; return [ xPos , yPos , zPos ] ; } param approach sort ( approach ) { if ( this . elements < 2 ) { return ; } const tuples = Array < [ GLsizei , number ] > ( this . elements ) ; for ( let i = 0 ; i < this . elements ; ++ i ) { let value = 0.0 ; switch ( approach ) { case AbstractKernel . SortApproach . BySquaredLength : default : for ( let c = 0 ; c < this . _components ; ++ c ) { const v = this . _samples [ i * this . _components + c ] ; value += v * v ; } break ; } tuples [ i ] = [ i , value ] ; } switch ( approach ) { case AbstractKernel . SortApproach . BySquaredLength : default : tuples . sort ( ( a , b ) => a [ 1 ] - b [ 1 ] ) ; break ; } const source = this . _samples . slice ( ) ; for ( let i = 0 ; i < tuples . length ; ++ i ) { for ( let c = 0 ; c < this . _components ; ++ c ) { const iSource = tuples [ i ] [ 0 ] * this . _components + c ; const iTarget = i * this . _components + c ; this . _samples [ iTarget ] = source [ iSource ] ; } } } /**
     * All elements/samples of the kernel as array buffer.
     */ get samples ( ) : T { return this . _samples ; } /**
     * Returns the number of samples, i.e., the number of elements times the number of components per element.
     */ get length ( ) : GLsizei { return this . _width * this . _height * this . _depth * this . _components ; } /**
     * Returns the number of samples.
     */ get elements ( ) : GLsizei { const elementCount = this . _width * this . _height * this . _depth ; return isNaN ( elementCount ) ? 0 : elementCount ; } /**
     * Number of components per sample, e.g., 2 for 2-tuple samples, 3 for 3-tuple samples, etc.
     */ get components ( ) : GLsizei { return this . _components ; } /**
     * The width of the kernel (x-axis)
     */ get width ( ) : GLsizei { return this . _width ; } /**
     * The height of the kernel (y-axis)
     */ get height ( ) : GLsizei { return this . _height ; } /**
     * The depth of the kernel (z-axis)
     */ get depth ( ) : GLsizei { return this . _depth ; } /**
     * Distance between the indices of two adjacent elements along the x-axis in bytes.
     */ get xStride ( ) : GLsizei { return this . bytesPerComponent * this . _components ; } /**
     * Distance between the indices of two adjacent elements along the y-axis in bytes.
     */ get yStride ( ) : GLsizei { return this . bytesPerComponent * this . _components * this . _width ; } /**
     * Distance between the indices of two adjacent elements along the z-axis in bytes.
     */ get zStride ( ) : GLsizei { return this . bytesPerComponent * this . _components * this . _width * this . _depth ; } /**
     * Length of all samples in bytes.
     */ get bytesLength ( ) : GLsizei { return this . bytesPerComponent * this . _components * this . _width * this . _height * this . _depth ; } /**
     * Size of a sample's component in bytes.
     */ abstract get bytesPerComponent ( ) : GLsizei ; } export namespace AbstractKernel { export enum SortApproach { BySquaredLength } } export class KernelF32 extends AbstractKernel < Float32Array > { protected resize ( ) { this . _samples = new Float32Array ( this . length ) ; } param samples fromArray ( samples ) { assert ( samples . length === this . length , `expected samples length to match this kernel's length` ) ; this . _samples . set ( new Float32Array ( samples ) ) ; } get bytesPerComponent ( ) : GLsizei { return 4 ; } } export class KernelUI32 extends AbstractKernel < Uint32Array > { protected resize ( ) { this . _samples = new Uint32Array ( this . length ) ; } param samples fromArray ( samples ) { assert ( samples . length === this . length , `expected samples length to match this kernel's length` ) ; this . _samples . set ( new Uint32Array ( samples ) ) ; } get bytesPerComponent ( ) : GLsizei { return 4 ; } } export class KernelI32 extends AbstractKernel < Int32Array > { protected resize ( ) { this . _samples = new Int32Array ( this . length ) ; } param samples fromArray ( samples ) { assert ( samples . length === this . length , `expected samples length to match this kernel's length` ) ; this . _samples . set ( new Int32Array ( samples ) ) ; } get bytesPerComponent ( ) : GLsizei { return 4 ; } } export class KernelUI8 extends AbstractKernel < Uint8Array > { protected resize ( ) { this . _samples = new Uint8Array ( this . length ) ; } param samples fromArray ( samples ) { assert ( samples . length === this . length , `expected samples length to match this kernel's length` ) ; this . _samples . set ( new Uint8Array ( samples ) ) ; } get bytesPerComponent ( ) : GLsizei { return 4 ; } } export class KernelI8 extends AbstractKernel < Int8Array > { protected resize ( ) { this . _samples = new Int8Array ( this . length ) ; } param samples fromArray ( samples ) { assert ( samples . length === this . length , `expected samples length to match this kernel's length` ) ; this . _samples . set ( new Int8Array ( samples ) ) ; } get bytesPerComponent ( ) : GLsizei { return 4 ; } }