@@ -21,19 +21,58 @@ const GRAPH_DEFAULTS = Object.freeze({
2121} ) ;
2222
2323const GRAPH_PRESET_COLORS = Object . freeze ( {
24+ "kg:entailment" : "#5C677D" ,
25+ "http://ns.inria.fr/corese/kgram/entailment" : "#5C677D" ,
2426 "urn:corese:inference:rdfs" : "#7BC8A4" ,
2527 "urn:corese:inference:owlrl" : "#F6B26B" ,
2628 "urn:corese:inference:owlrl-lite" : "#9A86E8" ,
2729 "urn:corese:inference:owlrl-ext" : "#F08CA0"
2830} ) ;
2931
30- const GRAPH_COLOR_GENERATION = Object . freeze ( {
31- hueSlotCount : 30 ,
32- hueStride : 11 ,
33- hueRetryStep : 7 ,
34- minDistanceFromPresetHue : 18 ,
35- saturationLevels : [ 42 , 48 , 54 ] ,
36- lightnessLevels : [ 64 , 69 , 74 ]
32+ // Ordered palette for user named graphs.
33+ //
34+ // The first entries are deliberately spaced out for the common case where only
35+ // a few named graphs are visible. Preset system graphs do not consume this
36+ // palette, so user graph colors stay stable and predictable across reloads and
37+ // later rule activations.
38+ const GRAPH_NAMED_GRAPH_PALETTE = Object . freeze ( [
39+ "#0077BB" ,
40+ "#CC3311" ,
41+ "#228833" ,
42+ "#EE7733" ,
43+ "#33BBEE" ,
44+ "#6A3D9A" ,
45+ "#999933" ,
46+ "#8C564B" ,
47+ "#AA3377" ,
48+ "#332288" ,
49+ "#009988" ,
50+ "#B7791F" ,
51+ "#0B63A5" ,
52+ "#9E2A2B" ,
53+ "#0F766E" ,
54+ "#A61E4D" ,
55+ "#3B5BDB" ,
56+ "#15803D" ,
57+ "#17BECF" ,
58+ "#A16207" ,
59+ "#9467BD" ,
60+ "#CC6677" ,
61+ "#4B5563" ,
62+ "#44AA99" ,
63+ "#7C3AED" ,
64+ "#BCBD22" ,
65+ "#117733" ,
66+ "#882255"
67+ ] ) ;
68+
69+ const GRAPH_COLOR_FALLBACK = Object . freeze ( {
70+ fallbackHueSlotCount : 48 ,
71+ fallbackHueStride : 17 ,
72+ fallbackHueRetryStep : 11 ,
73+ minDistanceFromPresetHue : 14 ,
74+ saturationLevels : [ 70 , 60 , 82 ] ,
75+ lightnessLevels : [ 50 , 60 , 44 ]
3776} ) ;
3877
3978/**
@@ -143,7 +182,6 @@ class KGGraphVis extends HTMLElement {
143182 this . graphColorMap = new Map ( ) ;
144183 this . graphContextPrefixes = new Map ( ) ;
145184 this . defaultGraphColor = GRAPH_DEFAULTS . defaultGraphColor ;
146- this . reservedGraphHues = [ ] ;
147185
148186 // Performance optimization
149187 this . TICK_THROTTLE_SMALL = 16 ;
@@ -708,13 +746,6 @@ class KGGraphVis extends HTMLElement {
708746 return gid ;
709747 }
710748
711- resolveReservedGraphHues ( ) {
712- return Object . values ( GRAPH_PRESET_COLORS )
713- . map ( color => this . hexToHsl ( color ) )
714- . filter ( hsl => Number . isFinite ( hsl . h ) && hsl . s > 0 )
715- . map ( hsl => hsl . h ) ;
716- }
717-
718749 stableHash ( input ) {
719750 const value = String ( input ?? "" ) ;
720751 let hash = 2166136261 ;
@@ -730,26 +761,41 @@ class KGGraphVis extends HTMLElement {
730761 return raw > 180 ? 360 - raw : raw ;
731762 }
732763
733- isHueTooCloseToReserved ( candidateHue , minDistance = GRAPH_COLOR_GENERATION . minDistanceFromPresetHue ) {
734- if ( ! Array . isArray ( this . reservedGraphHues ) || this . reservedGraphHues . length === 0 ) {
735- return false ;
764+ isHueTooCloseToReserved ( candidateHue , minDistance = GRAPH_COLOR_FALLBACK . minDistanceFromPresetHue ) {
765+ return Object . values ( GRAPH_PRESET_COLORS ) . some ( colorHex => {
766+ const hsl = this . hexToHsl ( colorHex ) ;
767+ return Number . isFinite ( hsl . h )
768+ && hsl . s > 0
769+ && this . hueDistance ( candidateHue , hsl . h ) < minDistance ;
770+ } ) ;
771+ }
772+
773+ pickPaletteGraphColor ( ) {
774+ const usedHexes = new Set (
775+ Array . from ( this . graphColorMap . values ( ) , colorHex => String ( colorHex ?? "" ) . trim ( ) . toUpperCase ( ) )
776+ ) ;
777+
778+ for ( const colorHex of GRAPH_NAMED_GRAPH_PALETTE ) {
779+ if ( ! usedHexes . has ( colorHex ) ) {
780+ return colorHex ;
781+ }
736782 }
737- return this . reservedGraphHues . some ( reservedHue => this . hueDistance ( candidateHue , reservedHue ) < minDistance ) ;
783+ return null ;
738784 }
739785
740786 buildStableGraphColor ( graphId ) {
741787 const hueHash = this . stableHash ( `${ graphId } |h` ) ;
742788 const saturationHash = this . stableHash ( `${ graphId } |s` ) ;
743789 const lightnessHash = this . stableHash ( `${ graphId } |l` ) ;
744790
745- const config = GRAPH_COLOR_GENERATION ;
746- const hueStep = 360 / config . hueSlotCount ;
747- const baseHueIndex = ( hueHash * config . hueStride ) % config . hueSlotCount ;
791+ const config = GRAPH_COLOR_FALLBACK ;
792+ const hueStep = 360 / config . fallbackHueSlotCount ;
793+ const baseHueIndex = ( hueHash * config . fallbackHueStride ) % config . fallbackHueSlotCount ;
748794 const saturation = config . saturationLevels [ saturationHash % config . saturationLevels . length ] ;
749795 const lightness = config . lightnessLevels [ lightnessHash % config . lightnessLevels . length ] ;
750796
751- for ( let attempt = 0 ; attempt < config . hueSlotCount ; attempt += 1 ) {
752- const candidateIndex = ( baseHueIndex + attempt * config . hueRetryStep ) % config . hueSlotCount ;
797+ for ( let attempt = 0 ; attempt < config . fallbackHueSlotCount ; attempt += 1 ) {
798+ const candidateIndex = ( baseHueIndex + attempt * config . fallbackHueRetryStep ) % config . fallbackHueSlotCount ;
753799 const candidateHue = candidateIndex * hueStep ;
754800 if ( ! this . isHueTooCloseToReserved ( candidateHue ) ) {
755801 return this . hslToHex ( candidateHue , saturation , lightness ) ;
@@ -764,7 +810,7 @@ class KGGraphVis extends HTMLElement {
764810 }
765811
766812 /**
767- * Generate a deterministic color for each named graph.
813+ * Returns the stable session color for a graph.
768814 * @param {string } graphId - Graph identifier.
769815 * @returns {string } Hex color code.
770816 */
@@ -775,17 +821,14 @@ class KGGraphVis extends HTMLElement {
775821 }
776822 const graphColorKey = this . resolveGraphColorKey ( gid ) ;
777823
778- if ( this . reservedGraphHues . length === 0 ) {
779- this . reservedGraphHues = this . resolveReservedGraphHues ( ) ;
780- }
781-
782824 if ( ! this . graphColorMap . has ( graphColorKey ) ) {
783825 const presetColor = this . resolvePresetGraphColor ( graphColorKey ) ;
784826 if ( presetColor ) {
785827 this . graphColorMap . set ( graphColorKey , presetColor ) ;
786828 return presetColor ;
787829 }
788- this . graphColorMap . set ( graphColorKey , this . buildStableGraphColor ( graphColorKey ) ) ;
830+ const assignedColor = this . pickPaletteGraphColor ( ) ?? this . buildStableGraphColor ( graphColorKey ) ;
831+ this . graphColorMap . set ( graphColorKey , assignedColor ) ;
789832 }
790833 return this . graphColorMap . get ( graphColorKey ) ;
791834 }
@@ -2916,7 +2959,28 @@ class KGGraphVis extends HTMLElement {
29162959 safeSummary . linkCount = Number . isFinite ( summary . linkCount )
29172960 ? Math . max ( 0 , Math . floor ( summary . linkCount ) )
29182961 : 0 ;
2919- safeSummary . namedGraphs = Array . isArray ( summary . namedGraphs ) ? summary . namedGraphs : [ ] ;
2962+ safeSummary . namedGraphs = Array . isArray ( summary . namedGraphs )
2963+ ? summary . namedGraphs
2964+ . map ( namedGraph => {
2965+ const id = this . normalizeGraphId ( namedGraph ?. id ) ;
2966+ if ( id === GRAPH_DEFAULTS . defaultGraphId ) {
2967+ return null ;
2968+ }
2969+ return {
2970+ id,
2971+ nodeCount : Number . isFinite ( namedGraph ?. nodeCount )
2972+ ? Math . max ( 0 , Math . floor ( namedGraph . nodeCount ) )
2973+ : 0 ,
2974+ linkCount : Number . isFinite ( namedGraph ?. linkCount )
2975+ ? Math . max ( 0 , Math . floor ( namedGraph . linkCount ) )
2976+ : 0 ,
2977+ color : typeof namedGraph ?. color === "string" && namedGraph . color
2978+ ? namedGraph . color
2979+ : this . getGraphColor ( id )
2980+ } ;
2981+ } )
2982+ . filter ( namedGraph => namedGraph !== null )
2983+ : [ ] ;
29202984 const counts = summary . componentCounts ?? { } ;
29212985 safeSummary . componentCounts . resource = Number . isFinite ( counts . resource ) ? Math . max ( 0 , Math . floor ( counts . resource ) ) : 0 ;
29222986 safeSummary . componentCounts . literal = Number . isFinite ( counts . literal ) ? Math . max ( 0 , Math . floor ( counts . literal ) ) : 0 ;
@@ -2988,11 +3052,11 @@ class KGGraphVis extends HTMLElement {
29883052 } ) ;
29893053
29903054 summary . namedGraphs = [ ...namedGraphStats . values ( ) ]
3055+ . sort ( ( left , right ) => right . linkCount - left . linkCount || left . id . localeCompare ( right . id ) )
29913056 . map ( stat => ( {
29923057 ...stat ,
29933058 color : this . getGraphColor ( stat . id )
2994- } ) )
2995- . sort ( ( left , right ) => right . linkCount - left . linkCount || left . id . localeCompare ( right . id ) ) ;
3059+ } ) ) ;
29963060 return summary ;
29973061 }
29983062
0 commit comments