@@ -119,12 +119,13 @@ import {
119119} from "#src/util/color.js" ;
120120import type { Borrowed , Owned } from "#src/util/disposable.js" ;
121121import { RefCounted } from "#src/util/disposable.js" ;
122- import type { vec3 , vec4 } from "#src/util/geom.js" ;
122+ import { vec3 , vec4 } from "#src/util/geom.js" ;
123123import {
124124 parseArray ,
125125 parseUint64 ,
126126 verifyArray ,
127127 verifyFiniteNonNegativeFloat ,
128+ verifyFloat ,
128129 verifyObject ,
129130 verifyObjectAsMap ,
130131 verifyObjectProperty ,
@@ -147,11 +148,11 @@ export interface SegmentPropertyColor {
147148 property : string ;
148149 map ?: Map < string , string > ; // TODO is there a color type?
149150 options ?: {
150- min ? : number ;
151- max ? : number ;
152- minColor ? : string ;
153- maxColor ? : string ;
154- }
151+ min : number ;
152+ max : number ;
153+ minColor : string ;
154+ maxColor : string ;
155+ } ;
155156}
156157
157158export class SegmentationUserLayerGroupState
@@ -318,62 +319,91 @@ export class SegmentationUserLayerColorGroupState
318319 constructor ( public layer : SegmentationUserLayer ) {
319320 super ( ) ;
320321
322+ this . segmentPropertyColorsMap = this . registerDisposer (
323+ makeCachedDerivedWatchableValue (
324+ ( segmentPropertyMap , segmentPropertyColors ) => {
325+ console . log ( "update segmentPropertyColorsMap" ) ;
326+ segmentPropertyColors ;
327+ const map = new Uint64Map ( ) ;
328+ if ( ! segmentPropertyMap ) {
329+ return map ;
330+ }
321331
322- this . segmentPropertyColorsMap = this . registerDisposer ( makeCachedDerivedWatchableValue (
323- ( segmentPropertyMap , segmentPropertyColors ) => {
324- console . log ( 'update segmentPropertyColorsMap' ) ;
325- segmentPropertyColors ;
326- const map = new Uint64Map ( ) ;
327- if ( ! segmentPropertyMap ) {
328- return map ;
329- }
330-
331- const { tags : tagsProperty } = segmentPropertyMap ;
332+ const { tags : tagsProperty } = segmentPropertyMap ;
332333
333- console . log ( "tags" , segmentPropertyMap . tags ) ;
334+ console . log ( "tags" , segmentPropertyMap . tags ) ;
334335
335- for ( const propertyColor of segmentPropertyColors ) {
336- if ( propertyColor . type === "tag" && tagsProperty ) {
337- const { tags, values } = tagsProperty ;
336+ for ( const propertyColor of segmentPropertyColors ) {
337+ if ( propertyColor . type === "tag" && tagsProperty ) {
338+ const { tags, values } = tagsProperty ;
338339
339- // const colors = todo performance, cache colors as bigints
340+ // const colors = todo performance, cache colors as bigints
340341
341- const bigIntColors = new Map < string , bigint > ( ) ;
342- for ( const [ tag , colorString ] of propertyColor . map ! . entries ( ) ) {
343- const color = parseRGBColorSpecification ( colorString ) ;
344- bigIntColors . set ( tag , BigInt ( packColor ( color ) ) ) ;
345- console . log ( "tag color" , tag , colorString , color ) ;
346- }
342+ const bigIntColors = new Map < string , bigint > ( ) ;
343+ for ( const [ tag , colorString ] of propertyColor . map ! . entries ( ) ) {
344+ const color = parseRGBColorSpecification ( colorString ) ;
345+ bigIntColors . set ( tag , BigInt ( packColor ( color ) ) ) ;
346+ console . log ( "tag color" , tag , colorString , color ) ;
347+ }
347348
348- for ( const [ index , id ] of ( segmentPropertyMap . segmentPropertyMap . inlineProperties ?. ids || [ ] ) . entries ( ) ) {
349- if ( map . has ( id ) ) continue ; // priority first color match
350- const tagIndices = values [ index ] ;
351- const segmentTags = new Set (
352- tagIndices . split ( "" ) . map ( ( x ) => tags [ x . charCodeAt ( 0 ) ] ) ,
353- ) ;
354-
355- for ( const [ tag , color ] of bigIntColors . entries ( ) ) {
356- if ( segmentTags . has ( tag ) ) {
357- map . set ( id , color ) ;
358- break ;
359- }
349+ for ( const [ index , id ] of (
350+ segmentPropertyMap . segmentPropertyMap . inlineProperties ?. ids ||
351+ [ ]
352+ ) . entries ( ) ) {
353+ if ( map . has ( id ) ) continue ; // priority first color match
354+ const tagIndices = values [ index ] ;
355+ const segmentTags = new Set (
356+ tagIndices . split ( "" ) . map ( ( x ) => tags [ x . charCodeAt ( 0 ) ] ) ,
357+ ) ;
358+
359+ for ( const [ tag , color ] of bigIntColors . entries ( ) ) {
360+ if ( segmentTags . has ( tag ) ) {
361+ map . set ( id , color ) ;
362+ break ;
360363 }
361364 }
362- } else if ( propertyColor . type === "numeric" ) {
363- console . log ( "propety:" , propertyColor . property ) ;
364365 }
366+ } else if ( propertyColor . type === "numeric" ) {
367+
368+ const options = propertyColor . options ! ;
365369
366- /*
367- "type": "numeric",
368- "property": "Nvx",
369- */
370+
371+ const { numericalProperties} = segmentPropertyMap ;
372+
373+ const numericalProperty = numericalProperties . find ( x => x . id === propertyColor . property ) ;
374+
375+ const { min, max} = options ;
376+
377+ const minColor = parseRGBColorSpecification ( options . minColor ) ;
378+ const maxColor = parseRGBColorSpecification ( options . maxColor ) ;
379+
380+ if ( numericalProperty ) {
381+ const { values} = numericalProperty ;
382+ for ( const [ index , id ] of (
383+ segmentPropertyMap . segmentPropertyMap . inlineProperties ?. ids ||
384+ [ ]
385+ ) . entries ( ) ) {
386+ const value = values [ index ] ;
387+ if ( Number . isNaN ( value ) ) continue ;
388+ const valueClamped = Math . min ( Math . max ( value , min ) , max ) ;
389+ const valueNormalized = ( valueClamped - min ) / ( max - min ) ;
390+ const color = vec3 . create ( ) ;
391+ vec3 . lerp ( color , minColor , maxColor , valueNormalized ) ;
392+ map . set ( id , BigInt ( packColor ( color ) ) ) ;
393+ }
394+ }
370395 }
371- console . log ( 'map' , map . size ) ;
372- return map ;
373- } ,
374- [ this . layer . displayState . originalSegmentationGroupState . segmentPropertyMap ,
375- this . segmentPropertyColors ] ,
376- ) ) ;
396+ }
397+ console . log ( "map" , map . size ) ;
398+ return map ;
399+ } ,
400+ [
401+ this . layer . displayState . originalSegmentationGroupState
402+ . segmentPropertyMap ,
403+ this . segmentPropertyColors ,
404+ ] ,
405+ ) ,
406+ ) ;
377407
378408 const { specificationChanged } = this ;
379409 this . segmentColorHash . changed . add ( specificationChanged . dispatch ) ;
@@ -430,27 +460,60 @@ export class SegmentationUserLayerColorGroupState
430460 "property" ,
431461 verifyString ,
432462 ) ;
433- const map = verifyObjectProperty (
434- propertyColor ,
435- json_keys . MAP_JSON_KEY ,
436- ( x ) => {
437- verifyObject ( x ) ;
438- const res = new Map < string , string > ( ) ;
439- for ( const [ tag , value ] of Object . entries ( x ) ) {
440- const color = verifyString ( value ) ;
441- res . set ( tag , color ) ;
442- }
443- return res ;
444- } ,
445- ) ;
446-
447- if ( type !== "tag" ) throw new Error ( `unsupported type: ${ type } ` ) ;
448-
449- this . segmentPropertyColors . value . push ( {
450- type,
451- property,
452- map,
453- } ) ;
463+ if ( type === "tag" ) {
464+ const map = verifyObjectProperty (
465+ propertyColor ,
466+ json_keys . MAP_JSON_KEY ,
467+ ( x ) => {
468+ verifyObject ( x ) ;
469+ const res = new Map < string , string > ( ) ;
470+ for ( const [ tag , value ] of Object . entries ( x ) ) {
471+ const color = verifyString ( value ) ;
472+ res . set ( tag , color ) ;
473+ }
474+ return res ;
475+ } ,
476+ ) ;
477+ this . segmentPropertyColors . value . push ( {
478+ type,
479+ property,
480+ map,
481+ } ) ;
482+ continue ;
483+ } else if ( type === "numeric" ) {
484+ const options = verifyObjectProperty (
485+ propertyColor ,
486+ "options" ,
487+ ( x ) => {
488+ verifyObject ( x ) ;
489+ const min = verifyObjectProperty ( x , "min" , verifyFloat ) ;
490+ const max = verifyObjectProperty ( x , "max" , verifyFloat ) ;
491+ const minColor = verifyObjectProperty (
492+ x ,
493+ "minColor" ,
494+ verifyString ,
495+ ) ;
496+ const maxColor = verifyObjectProperty (
497+ x ,
498+ "maxColor" ,
499+ verifyString ,
500+ ) ;
501+ return {
502+ min,
503+ max,
504+ minColor,
505+ maxColor,
506+ } ;
507+ } ,
508+ ) ;
509+ this . segmentPropertyColors . value . push ( {
510+ type,
511+ property,
512+ options,
513+ } ) ;
514+ } else {
515+ throw new Error ( `unsupported type: ${ type } ` ) ;
516+ }
454517 }
455518 this . segmentPropertyColors . changed . dispatch ( ) ;
456519 } ,
@@ -462,7 +525,10 @@ export class SegmentationUserLayerColorGroupState
462525 x [ json_keys . COLOR_SEED_JSON_KEY ] = this . segmentColorHash . toJSON ( ) ;
463526 x [ json_keys . SEGMENT_DEFAULT_COLOR_JSON_KEY ] =
464527 this . segmentDefaultColor . toJSON ( ) ;
465- const { segmentStatedColors, segmentPropertyColors : { value : segmentPropertyColors } } = this ;
528+ const {
529+ segmentStatedColors,
530+ segmentPropertyColors : { value : segmentPropertyColors } ,
531+ } = this ;
466532 if ( segmentStatedColors . size > 0 ) {
467533 const j : any = ( x [ json_keys . SEGMENT_STATED_COLORS_JSON_KEY ] = { } ) ;
468534 for ( const [ key , value ] of segmentStatedColors ) {
@@ -482,6 +548,7 @@ export class SegmentationUserLayerColorGroupState
482548 }
483549 } else {
484550 // TODO numeric
551+ p [ "options" ] = propertyColor . options ;
485552 }
486553 j . push ( p ) ;
487554 }
0 commit comments