Skip to content

Commit 6ca48ff

Browse files
committed
feat: Enhance contour computation with color mapping parameters and improve level handling
1 parent 0d086bc commit 6ca48ff

2 files changed

Lines changed: 35 additions & 3 deletions

File tree

dvue/animator/multi_ui.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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."""

dvue/animator/ui.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ def _run_contour_computation(
256256
xs_out, ys_out, lvl_out = [], [], []
257257
fig, ax = plt.subplots(1, 1)
258258
try:
259+
# Guard: matplotlib requires strictly increasing levels.
260+
levels_arr = np.unique(np.asarray(levels_arr, dtype=float))
261+
if len(levels_arr) < 2:
262+
return [], [], []
259263
cs = ax.contour(grid_x, grid_y, grid_z, levels=levels_arr)
260264
if hasattr(cs, "allsegs"):
261265
for i, lvl in enumerate(cs.levels):

0 commit comments

Comments
 (0)