@@ -170,16 +170,99 @@ function syncControls(config) {
170170 controls . json . value = JSON . stringify ( config , null , 2 ) ;
171171}
172172
173+ // Interpolate between two hex colors
174+ function interpolateColor ( color1 , color2 , factor ) {
175+ const c1 = parseInt ( color1 . slice ( 1 ) , 16 ) ;
176+ const c2 = parseInt ( color2 . slice ( 1 ) , 16 ) ;
177+ const r1 = ( c1 >> 16 ) & 255 , g1 = ( c1 >> 8 ) & 255 , b1 = c1 & 255 ;
178+ const r2 = ( c2 >> 16 ) & 255 , g2 = ( c2 >> 8 ) & 255 , b2 = c2 & 255 ;
179+ const r = Math . round ( r1 + ( r2 - r1 ) * factor ) ;
180+ const g = Math . round ( g1 + ( g2 - g1 ) * factor ) ;
181+ const b = Math . round ( b1 + ( b2 - b1 ) * factor ) ;
182+ return `#${ ( ( r << 16 ) | ( g << 8 ) | b ) . toString ( 16 ) . padStart ( 6 , '0' ) } ` ;
183+ }
184+
185+ // Get color from gradient at a given position (0-1)
186+ function getGradientColor ( position , colors , positions ) {
187+ if ( ! colors || colors . length === 0 ) return '#ffffff' ;
188+ if ( colors . length === 1 ) return colors [ 0 ] ;
189+
190+ // Normalize position to 0-1
191+ const t = Math . max ( 0 , Math . min ( 1 , position ) ) ;
192+
193+ // Find the two stops this position falls between
194+ let startIdx = 0 ;
195+ for ( let i = 0 ; i < positions . length - 1 ; i ++ ) {
196+ const pos1 = positions [ i ] / 100 ;
197+ const pos2 = positions [ i + 1 ] / 100 ;
198+ if ( t >= pos1 && t <= pos2 ) {
199+ startIdx = i ;
200+ break ;
201+ }
202+ }
203+
204+ const pos1 = positions [ startIdx ] / 100 ;
205+ const pos2 = positions [ startIdx + 1 ] / 100 ;
206+ const range = pos2 - pos1 ;
207+ const factor = range === 0 ? 0 : ( t - pos1 ) / range ;
208+
209+ return interpolateColor ( colors [ startIdx ] , colors [ startIdx + 1 ] , factor ) ;
210+ }
211+
173212function colorFor ( value , config , rowIndex , colIndex , channels ) {
174- const palette = config . rendu . palette || [ "#07121f" , "#ff9d4d" , "#53b0ff" , "#f5f7ff" ] ;
175213 const visualValue = channels && channels . length > 1 ? channels [ 1 ] : value ;
176214 const alphabet = config . alphabet_sortie || config . alphabet_entree || [ 0 , 1 ] ;
177215 let index = alphabet . findIndex ( ( item ) => String ( item ) === String ( visualValue ) ) ;
216+
217+ // Use gradient if available
218+ const gradient = config . rendu . gradient ;
219+ if ( gradient && gradient . colors && gradient . colors . length > 0 ) {
220+ let position = 0 ;
221+ if ( index >= 0 ) {
222+ position = index / Math . max ( 1 , alphabet . length - 1 ) ;
223+ } else {
224+ position = ( Math . abs ( Number ( visualValue ) || Number ( value ) || 0 ) % 100 ) / 100 ;
225+ }
226+
227+ // Apply gradient based on type
228+ if ( gradient . type === 'linear' ) {
229+ position = ( colIndex / ( config . largeur || 100 ) ) * 0.5 + ( rowIndex / ( config . hauteur || 100 ) ) * 0.5 ;
230+ } else if ( gradient . type === 'radial' ) {
231+ const centerX = ( config . largeur || 100 ) / 2 ;
232+ const centerY = ( config . hauteur || 100 ) / 2 ;
233+ const dist = Math . sqrt ( Math . pow ( colIndex - centerX , 2 ) + Math . pow ( rowIndex - centerY , 2 ) ) ;
234+ const maxDist = Math . sqrt ( Math . pow ( centerX , 2 ) + Math . pow ( centerY , 2 ) ) ;
235+ position = Math . min ( 1 , dist / maxDist ) ;
236+ } else if ( gradient . type === 'conic' ) {
237+ const centerX = ( config . largeur || 100 ) / 2 ;
238+ const centerY = ( config . hauteur || 100 ) / 2 ;
239+ const angle = Math . atan2 ( rowIndex - centerY , colIndex - centerX ) ;
240+ position = ( ( angle + Math . PI ) / ( 2 * Math . PI ) ) ;
241+ }
242+
243+ return getGradientColor ( position , gradient . colors , gradient . positions ) ;
244+ }
245+
246+ // Fallback to palette if no gradient
247+ const palette = config . rendu . palette || [ "#07121f" , "#ff9d4d" , "#53b0ff" , "#f5f7ff" ] ;
178248 if ( index < 0 ) index = Math . abs ( Number ( visualValue ) || Number ( value ) || 0 ) % palette . length ;
179249 const drift = ( rowIndex + colIndex ) % Math . max ( 1 , palette . length - 1 ) ;
180250 return palette [ ( index + drift ) % palette . length ] || palette [ 0 ] ;
181251}
182252
253+ window . applyGradientToPalette = function ( colors , type , positions ) {
254+ if ( ! state . config ) return ;
255+ state . config . rendu = state . config . rendu || { } ;
256+ state . config . rendu . gradient = {
257+ colors : colors || [ "#07121f" , "#ff9d4d" , "#53b0ff" , "#f5f7ff" ] ,
258+ type : type || 'linear' ,
259+ positions : positions || [ 0 , 50 , 100 ]
260+ } ;
261+ // Keep palette as fallback
262+ state . config . rendu . palette = colors || [ "#07121f" , "#ff9d4d" , "#53b0ff" , "#f5f7ff" ] ;
263+ render ( ) ;
264+ } ;
265+
183266function drawUniverseToCanvas ( targetCanvas , universe , maxWidth = 240 , maxHeight = 132 ) {
184267 const { configuration, lignes, sorties } = universe ;
185268 const context = targetCanvas . getContext ( "2d" ) ;
0 commit comments