@@ -820,8 +820,20 @@ def _recompute_contours(
820820 vmin : float ,
821821 vmax : float ,
822822 colormap : str ,
823+ color_vmin : "float | None" = None ,
824+ color_vmax : "float | None" = None ,
823825 ) -> None :
824- """Recompute contour paths for one map and update its ColumnDataSources."""
826+ """Recompute contour paths for one map and update its ColumnDataSources.
827+
828+ Parameters
829+ ----------
830+ vmin, vmax :
831+ Range used for contour *level* computation.
832+ color_vmin, color_vmax :
833+ Range used for contour *colour* mapping. Defaults to ``vmin``/``vmax``
834+ when not supplied. Pass the mapper bounds here when the colour scale
835+ differs from the level range (e.g. the diff map).
836+ """
825837 vals_arr = np .asarray (vals , dtype = float )
826838 mask = np .isfinite (vals_arr )
827839 if mask .sum () < 4 :
@@ -840,7 +852,8 @@ def _recompute_contours(
840852 float (self .contour_smooth ), levels , ctour .clip_zone ,
841853 )
842854 colors = (
843- _level_colors (lvls , vmin , vmax , colormap )
855+ _level_colors (lvls , color_vmin if color_vmin is not None else vmin ,
856+ color_vmax if color_vmax is not None else vmax , colormap )
844857 if self ._contour_color
845858 else ["black" ] * len (lvls )
846859 )
@@ -935,8 +948,23 @@ def _update_diff_map(self, ts: pd.Timestamp) -> None:
935948 self ._mapper_diff .low = eff_vmin
936949 self ._mapper_diff .high = eff_vmax
937950 if self ._ctour_diff .renderer .visible :
951+ # Use the diff data's own range for contour level computation.
952+ # eff_vmin/eff_vmax are from the Appearance panel (absolute values
953+ # of A), which is far outside the diff value range and would
954+ # produce nonsensical or non-increasing contour levels.
955+ d = self ._diff_reader_cache
956+ if d is not None :
957+ absmax = max (abs (d .vmin ), abs (d .vmax ), 1e-9 )
958+ c_vmin , c_vmax = - absmax , absmax
959+ else :
960+ finite = np .asarray ([v for v in vals if np .isfinite (v )])
961+ if len (finite ) >= 2 and finite .min () < finite .max ():
962+ c_vmin , c_vmax = float (finite .min ()), float (finite .max ())
963+ else :
964+ c_vmin , c_vmax = eff_vmin , eff_vmax
938965 self ._recompute_contours (
939- self ._ctour_diff , vals , eff_vmin , eff_vmax , self .colormap )
966+ self ._ctour_diff , vals , c_vmin , c_vmax , self .colormap ,
967+ color_vmin = eff_vmin , color_vmax = eff_vmax )
940968
941969 def _apply_frame (self , idx : int , ts_str : str ) -> None :
942970 """All Bokeh mutations for one frame step — must run under document lock."""
0 commit comments