@@ -123,8 +123,11 @@ import type { vec3, vec4 } from "#src/util/geom.js";
123123import {
124124 parseArray ,
125125 parseUint64 ,
126+ verifyArray ,
126127 verifyFiniteNonNegativeFloat ,
128+ verifyObject ,
127129 verifyObjectAsMap ,
130+ verifyObjectProperty ,
128131 verifyOptionalObjectProperty ,
129132 verifyString ,
130133} from "#src/util/json.js" ;
@@ -139,6 +142,18 @@ import { ShaderControlState } from "#src/webgl/shader_ui_controls.js";
139142
140143const MAX_LAYER_BAR_UI_INDICATOR_COLORS = 6 ;
141144
145+ export interface SegmentPropertyColor {
146+ type : "tag" | "numeric" ;
147+ property : string ;
148+ map ?: Map < string , string > ; // TODO is there a color type?
149+ options ?: {
150+ min ?: number ;
151+ max ?: number ;
152+ minColor ?: string ;
153+ maxColor ?: string ;
154+ }
155+ }
156+
142157export class SegmentationUserLayerGroupState
143158 extends RefCounted
144159 implements SegmentationGroupState
@@ -302,9 +317,69 @@ export class SegmentationUserLayerColorGroupState
302317 specificationChanged = new Signal ( ) ;
303318 constructor ( public layer : SegmentationUserLayer ) {
304319 super ( ) ;
320+
321+
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+
333+ console . log ( "tags" , segmentPropertyMap . tags ) ;
334+
335+ for ( const propertyColor of segmentPropertyColors ) {
336+ if ( propertyColor . type === "tag" && tagsProperty ) {
337+ const { tags, values } = tagsProperty ;
338+
339+ // const colors = todo performance, cache colors as bigints
340+
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+ }
347+
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+ }
360+ }
361+ }
362+ } else if ( propertyColor . type === "numeric" ) {
363+ console . log ( "propety:" , propertyColor . property ) ;
364+ }
365+
366+ /*
367+ "type": "numeric",
368+ "property": "Nvx",
369+ */
370+ }
371+ console . log ( 'map' , map . size ) ;
372+ return map ;
373+ } ,
374+ [ this . layer . displayState . originalSegmentationGroupState . segmentPropertyMap ,
375+ this . segmentPropertyColors ] ,
376+ ) ) ;
377+
305378 const { specificationChanged } = this ;
306379 this . segmentColorHash . changed . add ( specificationChanged . dispatch ) ;
307380 this . segmentStatedColors . changed . add ( specificationChanged . dispatch ) ;
381+ this . segmentPropertyColors . changed . add ( specificationChanged . dispatch ) ;
382+ this . segmentPropertyColorsMap . changed . add ( specificationChanged . dispatch ) ;
308383 this . tempSegmentStatedColors2d . changed . add ( specificationChanged . dispatch ) ;
309384 this . segmentDefaultColor . changed . add ( specificationChanged . dispatch ) ;
310385 this . tempSegmentDefaultColor2d . changed . add ( specificationChanged . dispatch ) ;
@@ -330,39 +405,103 @@ export class SegmentationUserLayerColorGroupState
330405 parseRGBColorSpecification ( String ( x ) ) ,
331406 ) ;
332407 for ( const [ idStr , colorVec ] of result ) {
408+ console . log ( "stated color" , idStr , colorVec ) ;
333409 const id = parseUint64 ( idStr ) ;
334410 const color = BigInt ( packColor ( colorVec ) ) ;
335411 this . segmentStatedColors . set ( id , color ) ;
336412 }
337413 } ,
338414 ) ;
415+
416+ verifyOptionalObjectProperty (
417+ specification ,
418+ json_keys . SEGMENT_PROPERTY_COLORS_JSON_KEY ,
419+ ( value ) => {
420+ const segmentPropertyColors = verifyArray ( value ) ;
421+ for ( const propertyColor of segmentPropertyColors ) {
422+ verifyObject ( propertyColor ) ;
423+ const type = verifyObjectProperty (
424+ propertyColor ,
425+ "type" ,
426+ verifyString ,
427+ ) ;
428+ const property = verifyObjectProperty (
429+ propertyColor ,
430+ "property" ,
431+ verifyString ,
432+ ) ;
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+ } ) ;
454+ }
455+ this . segmentPropertyColors . changed . dispatch ( ) ;
456+ } ,
457+ ) ;
339458 }
340459
341460 toJSON ( ) {
342461 const x : any = { } ;
343462 x [ json_keys . COLOR_SEED_JSON_KEY ] = this . segmentColorHash . toJSON ( ) ;
344463 x [ json_keys . SEGMENT_DEFAULT_COLOR_JSON_KEY ] =
345464 this . segmentDefaultColor . toJSON ( ) ;
346- const { segmentStatedColors } = this ;
465+ const { segmentStatedColors, segmentPropertyColors : { value : segmentPropertyColors } } = this ;
347466 if ( segmentStatedColors . size > 0 ) {
348467 const j : any = ( x [ json_keys . SEGMENT_STATED_COLORS_JSON_KEY ] = { } ) ;
349468 for ( const [ key , value ] of segmentStatedColors ) {
350469 j [ key . toString ( ) ] = serializeColor ( unpackRGB ( Number ( value ) ) ) ;
351470 }
352471 }
472+ if ( segmentPropertyColors . length > 0 ) {
473+ const j : any [ ] = ( x [ json_keys . SEGMENT_PROPERTY_COLORS_JSON_KEY ] = [ ] ) ;
474+ for ( const propertyColor of segmentPropertyColors ) {
475+ const p : any = { } ;
476+ p [ "type" ] = propertyColor . type ;
477+ p [ "property" ] = propertyColor . property ;
478+ if ( propertyColor . type === "tag" ) {
479+ const map : any = ( p [ json_keys . MAP_JSON_KEY ] = { } ) ;
480+ for ( const [ key , value ] of propertyColor . map ! . entries ( ) ) {
481+ map [ key . toString ( ) ] = value ;
482+ }
483+ } else {
484+ // TODO numeric
485+ }
486+ j . push ( p ) ;
487+ }
488+ }
353489 return x ;
354490 }
355491
356492 assignFrom ( other : SegmentationUserLayerColorGroupState ) {
357493 this . segmentColorHash . value = other . segmentColorHash . value ;
358494 this . segmentStatedColors . assignFrom ( other . segmentStatedColors ) ;
495+ this . segmentPropertyColors . value = other . segmentPropertyColors . value ;
359496 this . tempSegmentStatedColors2d . assignFrom ( other . tempSegmentStatedColors2d ) ;
360497 this . segmentDefaultColor . value = other . segmentDefaultColor . value ;
361498 this . highlightColor . value = other . highlightColor . value ;
362499 }
363500
364501 segmentColorHash = SegmentColorHash . getDefault ( ) ;
365502 segmentStatedColors = this . registerDisposer ( new Uint64Map ( ) ) ;
503+ segmentPropertyColors = new WatchableValue < SegmentPropertyColor [ ] > ( [ ] ) ;
504+ segmentPropertyColorsMap : WatchableValueInterface < Uint64Map > ;
366505 tempSegmentStatedColors2d = this . registerDisposer ( new Uint64Map ( ) ) ;
367506 segmentDefaultColor = new TrackableOptionalRGB ( ) ;
368507 tempSegmentDefaultColor2d = new WatchableValue < vec3 | vec4 | undefined > (
@@ -494,6 +633,18 @@ class SegmentationUserLayerDisplayState implements SegmentationDisplayState {
494633 ( group ) => group . segmentStatedColors ,
495634 ) ,
496635 ) ;
636+ this . segmentPropertyColors = this . layer . registerDisposer (
637+ new IndirectTrackableValue (
638+ this . segmentationColorGroupState ,
639+ ( group ) => group . segmentPropertyColors ,
640+ ) ,
641+ ) ;
642+ this . segmentPropertyColorsMap = this . layer . registerDisposer (
643+ new IndirectTrackableValue (
644+ this . segmentationColorGroupState ,
645+ ( group ) => group . segmentPropertyColorsMap ,
646+ ) ,
647+ ) ;
497648 this . tempSegmentStatedColors2d = this . layer . registerDisposer (
498649 new IndirectTrackableValue (
499650 this . segmentationColorGroupState ,
@@ -576,6 +727,8 @@ class SegmentationUserLayerDisplayState implements SegmentationDisplayState {
576727 segmentColorHash : TrackableValueInterface < number > ;
577728 segmentStatedColors : WatchableValueInterface < Uint64Map > ;
578729 tempSegmentStatedColors2d : WatchableValueInterface < Uint64Map > ;
730+ segmentPropertyColors : WatchableValueInterface < SegmentPropertyColor [ ] > ;
731+ segmentPropertyColorsMap : WatchableValueInterface < Uint64Map > ;
579732 segmentDefaultColor : WatchableValueInterface < vec3 | undefined > ;
580733 tempSegmentDefaultColor2d : WatchableValueInterface < vec3 | vec4 | undefined > ;
581734 highlightColor : WatchableValueInterface < vec4 | undefined > ;
0 commit comments