三维显示与二维共用深度色标,配置名改为 Finger
箭头阈值写入设置;去掉网格告警。三维色标改为立刻采用二维量程(不再 P5/P95 缓变)。选择器显示 Finger。已用 pane UI、shared-scale 与 catalog 单测验证。
This commit is contained in:
@@ -263,6 +263,24 @@ def shader_source(profile: Profile, program: str) -> tuple[str, str]:
|
||||
return header + vertex, header + body
|
||||
|
||||
|
||||
def _finite_minmax(scalar, valid) -> tuple[float, float] | None:
|
||||
"""Finite minimum and maximum — the same interval ``FrameView.frame_depth_span`` uses."""
|
||||
import numpy as np
|
||||
|
||||
values = np.asarray(scalar, dtype=np.float64).reshape(-1)
|
||||
mask = np.asarray(valid, dtype=bool).reshape(-1)
|
||||
if values.size != mask.size:
|
||||
return None
|
||||
usable = values[mask & np.isfinite(values)]
|
||||
if usable.size == 0:
|
||||
return None
|
||||
lo = float(np.min(usable))
|
||||
hi = float(np.max(usable))
|
||||
if hi <= lo:
|
||||
hi = lo + 1.0
|
||||
return lo, hi
|
||||
|
||||
|
||||
def _valid_scalar_range(
|
||||
scalar, valid, *, signed: bool = False
|
||||
) -> tuple[float, float] | None:
|
||||
@@ -379,6 +397,10 @@ def _field_scalar_range(
|
||||
hi = float(shared_depth_scale[1])
|
||||
if hi > lo and not (math.isnan(lo) or math.isnan(hi)):
|
||||
return lo, hi
|
||||
if quantity == "depth_response":
|
||||
# Same interval as the 2D canvas (finite min/max). A P5/P95 blend here
|
||||
# made the 3D colour bar crawl toward a different number than the 2D label.
|
||||
return _finite_minmax(field.scalar, field.valid)
|
||||
measured = _valid_scalar_range(
|
||||
field.scalar,
|
||||
field.valid,
|
||||
|
||||
@@ -159,6 +159,9 @@ class ThreeDPanel(QFrame):
|
||||
#: prefs (or the dataclass defaults) so a colour-scale click cannot reset them.
|
||||
self._flow_grid_spacing = 10
|
||||
self._max_arrows = 400
|
||||
#: Depth colour span published by the 2D canvas. Shown here immediately so the
|
||||
#: two legends do not wait on the next GL paint (which is rate-limited).
|
||||
self._shared_depth_span: tuple[float, float] | None = None
|
||||
|
||||
root = QVBoxLayout(self)
|
||||
root.setContentsMargins(8, 6, 8, 6)
|
||||
@@ -490,7 +493,16 @@ class ThreeDPanel(QFrame):
|
||||
if quantity.startswith("scene3d."):
|
||||
quantity = descriptor.quantity
|
||||
quality = t(f"scene3d.quality_{descriptor.quality}")
|
||||
if effective is None or effective.scale_mode == "":
|
||||
shared = (
|
||||
self._shared_depth_span
|
||||
if str(descriptor.quantity) == "depth_response"
|
||||
else None
|
||||
)
|
||||
if shared is not None:
|
||||
lo, hi = shared
|
||||
elif effective is not None and effective.scale_mode:
|
||||
lo, hi = effective.scale_lower, effective.scale_upper
|
||||
else:
|
||||
self._set_legend(
|
||||
t(
|
||||
"scene3d.legend",
|
||||
@@ -503,7 +515,7 @@ class ThreeDPanel(QFrame):
|
||||
return
|
||||
mode = t(
|
||||
"scene3d.legend_auto"
|
||||
if effective.scale_mode == "automatic"
|
||||
if (effective is None or effective.scale_mode != "fixed")
|
||||
else "scene3d.legend_fixed"
|
||||
)
|
||||
self._set_legend(
|
||||
@@ -512,15 +524,25 @@ class ThreeDPanel(QFrame):
|
||||
quantity=quantity,
|
||||
unit=descriptor.unit,
|
||||
mode=mode,
|
||||
# Three significant digits: the footer is one line, and "0.033 – 0.90"
|
||||
# says as much as "0.03337 – 0.8993" while leaving room for the rest.
|
||||
lower=f"{effective.scale_lower:.3g}",
|
||||
upper=f"{effective.scale_upper:.3g}",
|
||||
lower=self._format_bound(lo),
|
||||
upper=self._format_bound(hi),
|
||||
quality=quality,
|
||||
)
|
||||
)
|
||||
self.legend_label.setVisible(self._show_diagnostics)
|
||||
|
||||
def set_shared_depth_scale(self, span: tuple[float, float] | None) -> None:
|
||||
"""Adopt the 2D canvas's depth colour span and refresh the legend now."""
|
||||
if span is None:
|
||||
self._shared_depth_span = None
|
||||
else:
|
||||
self._shared_depth_span = (float(span[0]), float(span[1]))
|
||||
self._render_scale_bounds()
|
||||
self._render_legend()
|
||||
|
||||
def _format_bound(self, value: float) -> str:
|
||||
return f"{float(value):.3f}"
|
||||
|
||||
def _render_scale_bounds(self) -> None:
|
||||
"""Show only the numeric bounds needed to read the color ramp."""
|
||||
descriptor = self._descriptor
|
||||
@@ -531,16 +553,20 @@ class ThreeDPanel(QFrame):
|
||||
upper = ""
|
||||
mode = str(self.scale_combo.currentData() or "fixed")
|
||||
effective = self._effective_scale
|
||||
if visible and mode == "fixed":
|
||||
lower = f"{self.lower_spin.value():.3g}"
|
||||
upper = f"{self.upper_spin.value():.3g}"
|
||||
quantity = "" if descriptor is None else str(descriptor.quantity)
|
||||
if visible and quantity == "depth_response" and self._shared_depth_span is not None:
|
||||
lower = self._format_bound(self._shared_depth_span[0])
|
||||
upper = self._format_bound(self._shared_depth_span[1])
|
||||
elif visible and mode == "fixed":
|
||||
lower = self._format_bound(self.lower_spin.value())
|
||||
upper = self._format_bound(self.upper_spin.value())
|
||||
elif (
|
||||
visible
|
||||
and effective is not None
|
||||
and effective.scale_mode == "automatic"
|
||||
):
|
||||
lower = f"{effective.scale_lower:.3g}"
|
||||
upper = f"{effective.scale_upper:.3g}"
|
||||
lower = self._format_bound(effective.scale_lower)
|
||||
upper = self._format_bound(effective.scale_upper)
|
||||
self.scale_lower_label.setText(lower)
|
||||
self.scale_upper_label.setText(upper)
|
||||
self.scale_lower_label.setVisible(visible)
|
||||
|
||||
@@ -317,6 +317,8 @@ class Scene3DView(QWidget):
|
||||
"""Colour depth with the 2D canvas's latched ``(lo, hi)``."""
|
||||
if self._widget is not None:
|
||||
self._widget.set_shared_depth_scale(span)
|
||||
if self._panel is not None:
|
||||
self._panel.set_shared_depth_scale(span)
|
||||
|
||||
def set_theme(self, theme: str) -> None:
|
||||
"""Adopt the application theme's colour ramp.
|
||||
|
||||
Reference in New Issue
Block a user