@@ -226,7 +226,7 @@ def update_color_preset(
226226 else :
227227 linthresh = 1.0
228228
229- n_sub = max (1 , min (5 , int (n_discrete_colors )))
229+ n_sub = max (1 , min (20 , int (n_discrete_colors )))
230230 if log_scale == "linear" and discrete_log :
231231 display_rgb_points = self ._apply_discrete_linear_to_lut (
232232 linear_rgb_points , n_sub
@@ -560,18 +560,24 @@ def symlog(v):
560560 # Sample RGB from the linear CTF at symlog-normalized positions
561561 rgb = [0.0 , 0.0 , 0.0 ]
562562 new_rgb_points = []
563+ display_rgb_points = []
563564 for v in breakpoints :
564565 t = (float (symlog (v )) - s_min ) / s_range
565566 x_lookup = x_min + t * data_range
566567 linear_ctf .GetColor (x_lookup , rgb )
567- new_rgb_points .extend (
568- [float (v ), float (rgb [0 ]), float (rgb [1 ]), float (rgb [2 ])]
569- )
568+ r , g , b = float (rgb [0 ]), float (rgb [1 ]), float (rgb [2 ])
569+ new_rgb_points .extend ([float (v ), r , g , b ])
570+ # Display points: uniform linear positions with symlog colors
571+ display_rgb_points .extend ([x_lookup , r , g , b ])
572+
573+ # Regenerate colorbar image from display points so it matches the 3D
574+ self .lut .UseLogScale = 0
575+ self .lut .RGBPoints = display_rgb_points
576+ self .config .lut_img = lut_to_img (self .lut )
570577
571- # Store on proxy for bookkeeping — the actual CTF used by the
578+ # Store rendering points on proxy — the actual CTF used by the
572579 # mapper is a standalone vtkColorTransferFunction built in
573580 # update_color_preset to avoid proxy client-side object issues.
574- self .lut .UseLogScale = 0
575581 self .lut .RGBPoints = new_rgb_points
576582
577583 def _apply_discrete_symlog_to_lut (self , linthresh , linear_rgb_points , n_sub = 1 ):
@@ -666,7 +672,8 @@ def symlog(v):
666672 else :
667673 self ._discrete_tick_data = all_tick_data
668674
669- # Build a temporary linear CTF from the saved linear RGB points
675+ # Build a continuous symlog CTF (same as _apply_symlog_to_lut) so
676+ # discrete bands sample colours that match the continuous rendering.
670677 from vtkmodules .vtkRenderingCore import vtkColorTransferFunction
671678
672679 linear_ctf = vtkColorTransferFunction ()
@@ -678,12 +685,24 @@ def symlog(v):
678685 linear_rgb_points [i + 3 ],
679686 )
680687
688+ n_samples = 256
689+ s_vals = np .linspace (s_min , s_max , n_samples )
690+ symlog_ctf = vtkColorTransferFunction ()
691+ rgb_tmp = [0.0 , 0.0 , 0.0 ]
692+ for s in s_vals :
693+ v = float (np .sign (s ) * linthresh * (10.0 ** abs (s ) - 1.0 ))
694+ v = max (x_min , min (x_max , v ))
695+ t = (s - s_min ) / s_range
696+ x_lookup = x_min + t * data_range
697+ linear_ctf .GetColor (x_lookup , rgb_tmp )
698+ symlog_ctf .AddRGBPoint (v , rgb_tmp [0 ], rgb_tmp [1 ], rgb_tmp [2 ])
699+
681700 # For each decade interval, split into n_sub equal sub-bands in
682701 # symlog space. Each sub-band gets a flat color sampled from the
683- # continuous LUT at the sub-band midpoint.
702+ # continuous symlog LUT at the sub-band midpoint.
684703 rgb = [0.0 , 0.0 , 0.0 ]
685704 eps_data = (x_max - x_min ) * 1e-9
686- eps_lin = 1e-9
705+ eps_lin = data_range * 1e-9
687706 display_rgb_points = []
688707 render_rgb_points = []
689708 band_idx = 0
@@ -696,9 +715,11 @@ def symlog(v):
696715 s_lo = s_lo_decade + (s_hi_decade - s_lo_decade ) * j / n_sub
697716 s_hi = s_lo_decade + (s_hi_decade - s_lo_decade ) * (j + 1 ) / n_sub
698717 s_mid = (s_lo + s_hi ) / 2.0
699- t_mid = (s_mid - s_min ) / s_range
700- x_lookup = x_min + t_mid * data_range
701- linear_ctf .GetColor (x_lookup , rgb )
718+
719+ # Invert symlog to get data-space values
720+ v_mid = float (np .sign (s_mid ) * linthresh * (10.0 ** abs (s_mid ) - 1.0 ))
721+ v_mid = max (x_min , min (x_max , v_mid ))
722+ symlog_ctf .GetColor (v_mid , rgb )
702723 r , g , b = float (rgb [0 ]), float (rgb [1 ]), float (rgb [2 ])
703724
704725 # Invert symlog to get data-space boundaries for rendering
0 commit comments