import { Vector3 } from "three" ; import Channel from "./Channel" ; import Histogram from "./Histogram" ; import { getColorByChannelIndex } from "./constants/colors" ; export interface ImageInfo { name : string ; version : string ; width : number ; height : number ; channels : number ; tiles : number ; pixel_size_x : number ; pixel_size_y : number ; pixel_size_z : number ; channel_names : string [ ] ; channel_colors ? : [ number , number , number ] [ ] ; rows : number ; cols : number ; tile_width : number ; tile_height : number ; atlas_width : number ; atlas_height : number ; transform : { translation : [ number , number , number ] ; rotation : [ number , number , number ] ; } ; } export const getDefaultImageInfo = ( ) : ImageInfo => { return { name : "" , version : "" , width : 1 , height : 1 , channels : 0 , tiles : 1 , pixel_size_x : 1 , pixel_size_y : 1 , pixel_size_z : 1 , channel_names : [ ] , channel_colors : [ ] , rows : 1 , cols : 1 , tile_width : 1 , tile_height : 1 , atlas_width : 1 , atlas_height : 1 , transform : { translation : [ 0 , 0 , 0 ] , rotation : [ 0 , 0 , 0 ] , } , } ; } ; interface VolumeDataObserver { onVolumeData : ( vol : Volume , batch : number [ ] ) => void ; onVolumeChannelAdded : ( vol : Volume , idx : number ) => void ; } typedef ImageInfo property { string } name property { string } version property { number } width property { number } height property { number } channels property { number } tiles property { number } pixel_size_x property { number } pixel_size_y property { number } pixel_size_z property { Array . < string > } channel_names property { number } rows property { number } cols property { number } tile_width property { number } tile_height property { number } atlas_width property { number } atlas_height property { Object } transform example class param { ImageInfo } imageInfo export default class Volume { public imageInfo : ImageInfo ; public name : string ; public x : number ; public y : number ; public z : number ; private t : number ; private atlasSize : [ number , number ] ; private volumeSize : [ number , number , number ] ; public channels : Channel [ ] ; private volumeDataObservers : VolumeDataObserver [ ] ; public scale : Vector3 ; private currentScale : Vector3 ; private physicalSize : Vector3 ; public normalizedPhysicalSize : Vector3 ; private loaded : boolean ; public num_channels : number ; public channel_names : string [ ] ; public channel_colors_default : [ number , number , number ] [ ] ; private pixel_size : [ number , number , number ] ; constructor ( imageInfo = getDefaultImageInfo ( ) ) { this . scale = new Vector3 ( 1 , 1 , 1 ) ; this . currentScale = new Vector3 ( 1 , 1 , 1 ) ; this . physicalSize = new Vector3 ( 1 , 1 , 1 ) ; this . normalizedPhysicalSize = new Vector3 ( 1 , 1 , 1 ) ; this . loaded = false ; this . imageInfo = imageInfo ; this . name = this . imageInfo . name ; this . imageInfo . pixel_size_x = imageInfo . pixel_size_x || 1.0 ; this . imageInfo . pixel_size_y = imageInfo . pixel_size_y || 1.0 ; this . imageInfo . pixel_size_z = imageInfo . pixel_size_z || 1.0 ; this . pixel_size = [ this . imageInfo . pixel_size_x , this . imageInfo . pixel_size_y , this . imageInfo . pixel_size_z ] ; this . x = this . imageInfo . tile_width ; this . y = this . imageInfo . tile_height ; this . z = this . imageInfo . tiles ; this . t = 1 ; this . num_channels = this . imageInfo . channels ; this . channel_names = this . imageInfo . channel_names . slice ( ) ; this . channel_colors_default = this . imageInfo . channel_colors ? this . imageInfo . channel_colors . slice ( ) : this . channel_names . map ( ( name , index ) => getColorByChannelIndex ( index ) ) ; if ( this . channel_colors_default . length < this . num_channels ) { for ( let i = this . channel_colors_default . length - 1 ; i < this . num_channels ; ++ i ) { this . channel_colors_default [ i ] = getColorByChannelIndex ( i ) ; } } this . atlasSize = [ this . imageInfo . atlas_width , this . imageInfo . atlas_height ] ; this . volumeSize = [ this . x , this . y , this . z ] ; this . channels = [ ] ; for ( let i = 0 ; i < this . num_channels ; ++ i ) { const channel = new Channel ( this . channel_names [ i ] ) ; this . channels . push ( channel ) ; channel . dims = [ this . x , this . y , this . z ] ; } this . setVoxelSize ( this . pixel_size ) ; if ( ! this . imageInfo . transform ) { this . imageInfo . transform = { translation : [ 0 , 0 , 0 ] , rotation : [ 0 , 0 , 0 ] , } ; } if ( ! this . imageInfo . transform . translation ) { this . imageInfo . transform . translation = [ 0 , 0 , 0 ] ; } if ( ! this . imageInfo . transform . rotation ) { this . imageInfo . transform . rotation = [ 0 , 0 , 0 ] ; } this . volumeDataObservers = [ ] ; } setScale ( scale ) { this . scale = scale ; this . currentScale = scale . clone ( ) ; } setVoxelSize ( values ) { if ( ! values . length || values . length < 3 ) { return ; } if ( values [ 0 ] > 0 ) { this . pixel_size [ 0 ] = values [ 0 ] ; } if ( values [ 1 ] > 0 ) { this . pixel_size [ 1 ] = values [ 1 ] ; } if ( values [ 2 ] > 0 ) { this . pixel_size [ 2 ] = values [ 2 ] ; } const physSizeMin = Math . min ( this . pixel_size [ 0 ] , Math . min ( this . pixel_size [ 1 ] , this . pixel_size [ 2 ] ) ) ; const pixelsMax = Math . max ( this . imageInfo . width , Math . max ( this . imageInfo . height , this . z ) ) ; const sx = ( ( this . pixel_size [ 0 ] / physSizeMin ) * this . imageInfo . width ) / pixelsMax ; const sy = ( ( this . pixel_size [ 1 ] / physSizeMin ) * this . imageInfo . height ) / pixelsMax ; const sz = ( ( this . pixel_size [ 2 ] / physSizeMin ) * this . z ) / pixelsMax ; this . physicalSize = new Vector3 ( this . imageInfo . width * this . pixel_size [ 0 ] , this . imageInfo . height * this . pixel_size [ 1 ] , this . z * this . pixel_size [ 2 ] ) ; const m = Math . max ( this . physicalSize . x , Math . max ( this . physicalSize . y , this . physicalSize . z ) ) ; this . normalizedPhysicalSize = new Vector3 ( ) . copy ( this . physicalSize ) . multiplyScalar ( 1.0 / m ) ; this . setScale ( new Vector3 ( sx , sy , sz ) ) ; } cleanup ( ) { } return channelNames ( ) { return this . channel_names ; } getChannel ( channelIndex ) { return this . channels [ channelIndex ] ; } onChannelLoaded ( batch ) { if ( this . channels . every ( function ( element , _index , _array ) { return element . loaded ; } ) ) { this . loaded = true ; } for ( let i = 0 ; i < this . volumeDataObservers . length ; ++ i ) { this . volumeDataObservers [ i ] . onVolumeData ( this , batch ) ; } } param { number } channelIndex param { Uint8Array } atlasdata param { number } atlaswidth param { number } atlasheight setChannelDataFromAtlas ( channelIndex , atlasdata , atlaswidth , atlasheight ) { this . channels [ channelIndex ] . setBits ( atlasdata , atlaswidth , atlasheight ) ; this . channels [ channelIndex ] . unpackVolumeFromAtlas ( this . x , this . y , this . z ) ; this . onChannelLoaded ( [ channelIndex ] ) ; } param { number } channelIndex param { Uint8Array } volumeData setChannelDataFromVolume ( channelIndex , volumeData ) { this . channels [ channelIndex ] . setFromVolumeData ( volumeData , this . x , this . y , this . z , this . atlasSize [ 0 ] , this . atlasSize [ 1 ] ) ; this . onChannelLoaded ( [ channelIndex ] ) ; } param { string } name param { Array . < number > } color appendEmptyChannel ( name , color ? ) { const idx = this . num_channels ; const chname = name || "channel_" + idx ; const chcolor = color || getColorByChannelIndex ( idx ) ; this . num_channels += 1 ; this . channel_names . push ( chname ) ; this . channel_colors_default . push ( chcolor ) ; this . channels . push ( new Channel ( chname ) ) ; for ( let i = 0 ; i < this . volumeDataObservers . length ; ++ i ) { this . volumeDataObservers [ i ] . onVolumeChannelAdded ( this , idx ) ; } return idx ; } return { number } param { number } c param { number } x param { number } y param { number } z getIntensity ( c , x , y , z ) { return this . channels [ c ] . getIntensity ( x , y , z ) ; } return { Histogram } param { number } c getHistogram ( c ) { return this . channels [ c ] . getHistogram ( ) ; } param { number } c param { Array . < number > } lut setLut ( c , lut ) { this . channels [ c ] . setLut ( lut ) ; } param { number } c param { Array . < number > } palette setColorPalette ( c , palette ) { this . channels [ c ] . setColorPalette ( palette ) ; } param { number } c param { number } alpha setColorPaletteAlpha ( c , alpha ) { this . channels [ c ] . setColorPaletteAlpha ( alpha ) ; } return { Array . < number > } getRotation ( ) { return this . imageInfo . transform . rotation ; } return { Array . < number > } getTranslation ( ) { return this . voxelsToWorldSpace ( this . imageInfo . transform . translation ) ; } return { Array . < number > } voxelsToWorldSpace ( xyz ) { const m = 1.0 / Math . max ( this . physicalSize . x , Math . max ( this . physicalSize . y , this . physicalSize . z ) ) ; const pixelSizeVec = new Vector3 ( ) . fromArray ( this . pixel_size ) ; return new Vector3 ( ) . fromArray ( xyz ) . multiply ( pixelSizeVec ) . multiplyScalar ( m ) . toArray ( ) ; } addVolumeDataObserver ( o ) { this . volumeDataObservers . push ( o ) ; } removeVolumeDataObserver ( o ) { if ( o ) { const i = this . volumeDataObservers . indexOf ( o ) ; if ( i !== - 1 ) { this . volumeDataObservers . splice ( i , 1 ) ; } } } removeAllVolumeDataObservers ( ) { this . volumeDataObservers = [ ] ; } isLoaded ( ) { return this . loaded ; } setIsLoaded ( loaded ) { this . loaded = loaded ; } }