@@ -29,37 +29,80 @@ export const confidenceColorScale: ColorStop[] = [
2929 { value : 0.58 , color : '#33a02c' , label : '58' } ,
3030]
3131
32- function hexToRgb ( hex : string ) : [ number , number , number ] {
33- return [
34- parseInt ( hex . slice ( 1 , 3 ) , 16 ) ,
35- parseInt ( hex . slice ( 3 , 5 ) , 16 ) ,
36- parseInt ( hex . slice ( 5 , 7 ) , 16 ) ,
37- ]
32+ const LUT_SIZE = 256
33+
34+ interface PrecomputedScale {
35+ lut : Uint8Array
36+ firstValue : number
37+ rangeInv : number
3838}
3939
40- export function getColorForValue ( colorScale : ColorStop [ ] , value : number , alpha = 1 ) : string {
41- const first = colorScale [ 0 ]
42- const last = colorScale [ colorScale . length - 1 ]
43- let hex : string
44- if ( value <= first . value ) {
45- hex = first . color
46- } else if ( value >= last . value ) {
47- hex = last . color
48- } else {
49- hex = last . color
50- for ( let i = 0 ; i < colorScale . length - 1 ; i ++ ) {
51- if ( value <= colorScale [ i + 1 ] . value ) {
52- const t = ( value - colorScale [ i ] . value ) / ( colorScale [ i + 1 ] . value - colorScale [ i ] . value )
53- const [ r1 , g1 , b1 ] = hexToRgb ( colorScale [ i ] . color )
54- const [ r2 , g2 , b2 ] = hexToRgb ( colorScale [ i + 1 ] . color )
55- const r = Math . round ( r1 + ( r2 - r1 ) * t )
56- const g = Math . round ( g1 + ( g2 - g1 ) * t )
57- const b = Math . round ( b1 + ( b2 - b1 ) * t )
58- hex = `#${ r . toString ( 16 ) . padStart ( 2 , '0' ) } ${ g . toString ( 16 ) . padStart ( 2 , '0' ) } ${ b . toString ( 16 ) . padStart ( 2 , '0' ) } `
59- break
40+ const scaleCache = new Map < ColorStop [ ] , PrecomputedScale > ( )
41+
42+ function precomputeScale ( colorScale : ColorStop [ ] ) : PrecomputedScale {
43+ const n = colorScale . length
44+ const stopValues = new Float64Array ( n )
45+ const stopR = new Uint8Array ( n )
46+ const stopG = new Uint8Array ( n )
47+ const stopB = new Uint8Array ( n )
48+ for ( let i = 0 ; i < n ; i ++ ) {
49+ const hex = colorScale [ i ] . color
50+ stopValues [ i ] = colorScale [ i ] . value
51+ stopR [ i ] = parseInt ( hex . slice ( 1 , 3 ) , 16 )
52+ stopG [ i ] = parseInt ( hex . slice ( 3 , 5 ) , 16 )
53+ stopB [ i ] = parseInt ( hex . slice ( 5 , 7 ) , 16 )
54+ }
55+
56+ const firstValue = stopValues [ 0 ]
57+ const range = stopValues [ n - 1 ] - firstValue
58+ const lut = new Uint8Array ( LUT_SIZE * 3 )
59+
60+ for ( let i = 0 ; i < LUT_SIZE ; i ++ ) {
61+ const value = firstValue + ( i / ( LUT_SIZE - 1 ) ) * range
62+ const offset = i * 3
63+
64+ let lo = 0
65+ let hi = n - 1
66+ if ( value <= stopValues [ 0 ] ) {
67+ lo = hi = 0
68+ } else if ( value >= stopValues [ n - 1 ] ) {
69+ lo = hi = n - 1
70+ } else {
71+ for ( let j = 0 ; j < n - 1 ; j ++ ) {
72+ if ( value <= stopValues [ j + 1 ] ) {
73+ lo = j
74+ hi = j + 1
75+ break
76+ }
6077 }
6178 }
79+
80+ if ( lo === hi ) {
81+ lut [ offset ] = stopR [ lo ]
82+ lut [ offset + 1 ] = stopG [ lo ]
83+ lut [ offset + 2 ] = stopB [ lo ]
84+ } else {
85+ const t = ( value - stopValues [ lo ] ) / ( stopValues [ hi ] - stopValues [ lo ] )
86+ lut [ offset ] = Math . round ( stopR [ lo ] + ( stopR [ hi ] - stopR [ lo ] ) * t )
87+ lut [ offset + 1 ] = Math . round ( stopG [ lo ] + ( stopG [ hi ] - stopG [ lo ] ) * t )
88+ lut [ offset + 2 ] = Math . round ( stopB [ lo ] + ( stopB [ hi ] - stopB [ lo ] ) * t )
89+ }
90+ }
91+
92+ return { lut, firstValue, rangeInv : ( LUT_SIZE - 1 ) / range }
93+ }
94+
95+ function getPrecomputed ( colorScale : ColorStop [ ] ) : PrecomputedScale {
96+ let cached = scaleCache . get ( colorScale )
97+ if ( ! cached ) {
98+ cached = precomputeScale ( colorScale )
99+ scaleCache . set ( colorScale , cached )
62100 }
63- const [ r , g , b ] = hexToRgb ( hex )
64- return `rgba(${ r } , ${ g } , ${ b } , ${ alpha } )`
101+ return cached
102+ }
103+
104+ export function getColorForValue ( colorScale : ColorStop [ ] , value : number , alpha = 1 ) : string {
105+ const { lut, firstValue, rangeInv } = getPrecomputed ( colorScale )
106+ const idx = Math . min ( Math . max ( Math . round ( ( value - firstValue ) * rangeInv ) , 0 ) , LUT_SIZE - 1 ) * 3
107+ return `rgba(${ lut [ idx ] } , ${ lut [ idx + 1 ] } , ${ lut [ idx + 2 ] } , ${ alpha } )`
65108}
0 commit comments