@@ -229,7 +229,7 @@ function getDefaultPalette(alphabetSize) {
229229 ) ;
230230}
231231
232- function colorFor ( value , config , channels ) {
232+ function colorFor ( value , config , channels , x = 0 , y = 0 , gridWidth = 1 , gridHeight = 1 ) {
233233 const visualValue = channels && channels . length > 1 ? channels [ 1 ] : value ;
234234 const alphabet = config . alphabet_sortie || config . alphabet_entree || [ 0 , 1 ] ;
235235 let index = alphabet . findIndex ( ( item ) => String ( item ) === String ( visualValue ) ) ;
@@ -238,19 +238,52 @@ function colorFor(value, config, channels) {
238238 // Check if gradient mode is enabled
239239 const gradient = config . rendu ?. gradient ;
240240 if ( gradient && gradient . mode === "gradient" ) {
241- return interpolateGradientColor ( index , alphabet . length , gradient ) ;
241+ return interpolateGradientColor ( index , alphabet . length , gradient , x , y , gridWidth , gridHeight ) ;
242242 }
243243
244244 // Fall back to discrete palette
245245 const palette = config . rendu . palette || getDefaultPalette ( alphabet . length ) ;
246246 return palette [ index % palette . length ] || palette [ 0 ] ;
247247}
248248
249- function interpolateGradientColor ( stateIndex , stateCount , gradientConfig ) {
250- const t = stateCount > 1 ? stateIndex / ( stateCount - 1 ) : 0 ;
249+ function positionToParameter ( x , y , gridWidth , gridHeight , direction = "haut-bas" ) {
250+ switch ( direction ) {
251+ case "gauche-droite" :
252+ return gridWidth > 1 ? x / ( gridWidth - 1 ) : 0 ;
253+ case "diagonal" :
254+ return gridWidth > 1 || gridHeight > 1 ? ( x / ( gridWidth - 1 || 1 ) + y / ( gridHeight - 1 || 1 ) ) / 2 : 0 ;
255+ case "radial" : {
256+ const cx = ( gridWidth - 1 ) / 2 ;
257+ const cy = ( gridHeight - 1 ) / 2 ;
258+ const maxDist = Math . sqrt ( cx * cx + cy * cy ) || 1 ;
259+ const dist = Math . sqrt ( ( x - cx ) ** 2 + ( y - cy ) ** 2 ) ;
260+ return Math . min ( 1 , dist / maxDist ) ;
261+ }
262+ case "haut-bas" :
263+ default :
264+ return gridHeight > 1 ? y / ( gridHeight - 1 ) : 0 ;
265+ }
266+ }
267+
268+ function interpolateGradientColor ( stateIndex , stateCount , gradientConfig , x = 0 , y = 0 , gridWidth = 1 , gridHeight = 1 ) {
251269 const anchors = gradientConfig . ancres || { } ;
252270 const method = gradientConfig . methode || "lineaire" ;
271+ const appliquer = gradientConfig . appliquer || "etat" ;
272+ const direction = gradientConfig . direction || "haut-bas" ;
273+ const influenceEtat = ( gradientConfig . influence_etat ?? 50 ) / 100 ;
274+
275+ // Calculate parameters based on mode
276+ let t_state = stateCount > 1 ? stateIndex / ( stateCount - 1 ) : 0 ;
277+ let t_position = positionToParameter ( x , y , gridWidth , gridHeight , direction ) ;
278+ let t = t_state ;
279+
280+ if ( appliquer === "position" ) {
281+ t = t_position ;
282+ } else if ( appliquer === "combine" ) {
283+ t = t_state * influenceEtat + t_position * ( 1 - influenceEtat ) ;
284+ }
253285
286+ // Apply interpolation based on method
254287 if ( method === "lineaire" && stateCount >= 2 ) {
255288 const c0 = anchors [ "0" ] || "#0066ff" ;
256289 const c1 = anchors [ "1" ] || "#ff6b35" ;
@@ -268,17 +301,36 @@ function interpolateGradientColor(stateIndex, stateCount, gradientConfig) {
268301 }
269302
270303 if ( method === "bilinear" && stateCount >= 4 ) {
304+ let u , v ;
305+ if ( appliquer === "position" ) {
306+ // For position-based, map position to 2D space
307+ u = gridWidth > 1 ? x / ( gridWidth - 1 ) : 0 ;
308+ v = gridHeight > 1 ? y / ( gridHeight - 1 ) : 0 ;
309+ } else if ( appliquer === "combine" ) {
310+ // For combined, blend state and position for each axis
311+ const u_state = ( stateIndex % 2 ) ;
312+ const v_state = Math . floor ( stateIndex / 2 ) % 2 ;
313+ u = u_state * influenceEtat + ( gridWidth > 1 ? x / ( gridWidth - 1 ) : 0 ) * ( 1 - influenceEtat ) ;
314+ v = v_state * influenceEtat + ( gridHeight > 1 ? y / ( gridHeight - 1 ) : 0 ) * ( 1 - influenceEtat ) ;
315+ } else {
316+ // State-based: use state index to determine grid position
317+ u = ( stateIndex % 2 ) ;
318+ v = Math . floor ( stateIndex / 2 ) % 2 ;
319+ }
271320 const c0 = anchors [ "0" ] || "#0066ff" ;
272321 const c1 = anchors [ "1" ] || "#00aaff" ;
273322 const c2 = anchors [ "2" ] || "#00ff41" ;
274323 const c3 = anchors [ "3" ] || "#ff6b35" ;
275- const u = t * 2 ;
276- const v = t * 2 ;
277- return bilinearColor ( u < 1 ? u : 2 - u , v < 1 ? v : 2 - v , c0 , c1 , c2 , c3 ) ;
324+ return bilinearColor ( u , v , c0 , c1 , c2 , c3 ) ;
278325 }
279326
280- // IDW fallback
327+ // IDW fallback for 5+ states
281328 const colors = Object . values ( anchors ) ;
329+ if ( appliquer === "position" ) {
330+ t = t_position ;
331+ } else if ( appliquer === "combine" ) {
332+ t = t_state * influenceEtat + t_position * ( 1 - influenceEtat ) ;
333+ }
282334 return idwColor ( t , stateCount , colors ) ;
283335}
284336
@@ -429,13 +481,15 @@ function render() {
429481 const cell = Number ( configuration . rendu . taille_cellule || 5 ) ;
430482 ctx . fillStyle = configuration . rendu . fond || "#05070d" ;
431483 ctx . fillRect ( 0 , 0 , canvas . width , canvas . height ) ;
484+ const gridHeight = lignes . length ;
485+ const gridWidth = lignes . length > 0 ? lignes [ 0 ] . length : 1 ;
432486 lignes . forEach ( ( line , y ) => {
433487 line . forEach ( ( value , x ) => {
434488 const channels = sorties ?. [ y ] ?. [ x ] ;
435489 const visualValue = channels && channels . length > 1 ? channels [ 1 ] : value ;
436490 const bgValue = configuration . alphabet_sortie ?. [ 0 ] ?? configuration . alphabet_entree [ 0 ] ;
437491 if ( ! configuration . rendu . afficher_zero && String ( visualValue ) === String ( bgValue ) ) return ;
438- ctx . fillStyle = colorFor ( value , configuration , channels ) ;
492+ ctx . fillStyle = colorFor ( value , configuration , channels , x , y , gridWidth , gridHeight ) ;
439493 ctx . fillRect ( x * cell , y * cell , cell , cell ) ;
440494 } ) ;
441495 } ) ;
@@ -476,9 +530,42 @@ function activateInitialMode(mode) {
476530 controls . initialMode . value = mode ;
477531}
478532
533+ function ensureGradientConfig ( config ) {
534+ if ( ! config . rendu ) {
535+ config . rendu = { } ;
536+ }
537+ if ( ! config . rendu . gradient ) {
538+ const alphabet = config . alphabet_sortie || config . alphabet_entree || [ 0 , 1 ] ;
539+ const stateCount = alphabet . length ;
540+ const defaultPalette = getDefaultPalette ( stateCount ) ;
541+ const defaultMethod = stateCount === 2 ? "lineaire" : stateCount === 3 ? "barycentric" : stateCount >= 4 ? "bilinear" : "idw" ;
542+
543+ config . rendu . gradient = {
544+ mode : "discret" ,
545+ appliquer : "etat" ,
546+ direction : "haut-bas" ,
547+ influence_etat : 50 ,
548+ methode : defaultMethod ,
549+ nombre_etats : stateCount ,
550+ ancres : { } ,
551+ } ;
552+
553+ for ( let i = 0 ; i < stateCount ; i ++ ) {
554+ config . rendu . gradient . ancres [ String ( i ) ] = defaultPalette [ i ] || "#808080" ;
555+ }
556+ } else {
557+ // Ensure all new fields exist in existing gradient configs
558+ if ( ! config . rendu . gradient . appliquer ) config . rendu . gradient . appliquer = "etat" ;
559+ if ( ! config . rendu . gradient . direction ) config . rendu . gradient . direction = "haut-bas" ;
560+ if ( config . rendu . gradient . influence_etat === undefined ) config . rendu . gradient . influence_etat = 50 ;
561+ }
562+ return config ;
563+ }
564+
479565function applyConfig ( config , { source = "Configuration" } = { } ) {
480566 try {
481567 const normalized = window . AutomaginariumCore . ensureRenderableConfiguration ( config ) ;
568+ ensureGradientConfig ( normalized ) ;
482569 const validation = validateConfig ( normalized ) ;
483570 showStatus ( validation ) ;
484571 if ( ! validation . valid ) {
0 commit comments