Added environment panel for depth, pressure and outside temperature.

This commit is contained in:
RedNicStone committed 2026-11-09 20:35:17 +01:00
1 parent 2d91487c6a
commit 92006372da
21 files changed
+2825 -1616

No files matched your search

+64
View File
@@ -0,0 +1,64 @@
@tool
extends Control
var depth_m := 0.0
var maximum_depth_m := 1000.0
var knee_m := 1.0
var warning_fraction := 0.8
var fault_fraction := 0.95
var available := false
func _ready() -> void:
resized.connect(queue_redraw)
theme_changed.connect(queue_redraw)
# log(1 + depth / knee) includes zero and is nearly linear close to the surface.
func normalized_depth(value: float) -> float:
var knee := maxf(knee_m, 0.001)
var maximum := maxf(maximum_depth_m, 0.001)
return log(1 + clampf(value, 0, maximum) / knee) / log(1 + maximum / knee)
func severity() -> int:
var ratio := depth_m / maxf(maximum_depth_m, 0.001)
return 2 if ratio >= fault_fraction else (1 if ratio >= warning_fraction else 0)
func _draw() -> void:
var top := 9.0
var bottom := maxf(top + 1, size.y - 10)
var track := get_theme_color("instrument_grid", "SurfaceSide")
var ink := get_theme_color("font_color", "Label")
var font := get_theme_default_font()
draw_line(Vector2(17, top), Vector2(17, bottom), track, 2, true)
var decade := maxf(knee_m, 0.001)
while decade < maximum_depth_m:
for multiplier in [1.0, 2.0, 5.0]:
var tick: float = decade * multiplier
if tick > 0 and tick < maximum_depth_m:
var y: float = lerpf(top, bottom, normalized_depth(tick))
draw_line(Vector2(14, y), Vector2(21, y), track, 1, true)
decade *= 10
var last_label_y := -100.0
var last_label_value := -1.0
var ticks: Array[float] = [0, maxf(knee_m, 0.001) * 2]
decade = maxf(knee_m, 0.001) * 10
while decade < maximum_depth_m:
ticks.append(decade)
decade *= 10
ticks.append(maximum_depth_m)
for tick in ticks:
if tick < 0 or tick > maximum_depth_m or is_equal_approx(tick, last_label_value):
continue
var y := lerpf(top, bottom, normalized_depth(tick))
if tick != maximum_depth_m and (y - last_label_y < 20 or (bottom - y < 20 and tick != 0)):
continue
draw_line(Vector2(12, y), Vector2(23, y), ink, 1, true)
draw_string(font, Vector2(29, y + 5), String.num(tick, 3).trim_suffix(".0"), HORIZONTAL_ALIGNMENT_LEFT, -1, 14, ink)
last_label_y = y
last_label_value = tick
if available:
var y := lerpf(top, bottom, normalized_depth(depth_m))
var role: String = ["instrument_accent", "warning", "fault"][severity()]
var accent := get_theme_color(role, "SurfaceSide")
draw_line(Vector2(17, top), Vector2(17, y), accent, 3, true)
draw_colored_polygon(PackedVector2Array([Vector2(1, y - 5), Vector2(1, y + 5), Vector2(12, y)]), accent)
draw_line(Vector2(12, y), Vector2(23, y), accent, 3, true)
+1
View File
@@ -0,0 +1 @@
uid://cbobdfddbbddo
+23
View File
@@ -0,0 +1,23 @@
extends VBoxContainer
var elapsed := 0.0
@onready var panel = $EnvironmentPanel
func _ready() -> void:
$Controls/Contrast.toggled.connect(func(high: bool) -> void:
theme = load("res://themes/theme_highcontrast.tres" if high else "res://themes/theme.tres")
)
$Controls/State.item_selected.connect(func(index: int) -> void:
if index == 7: panel.clear_telemetry()
)
func _process(delta: float) -> void:
elapsed += delta
match $Controls/State.selected:
0: panel.set_telemetry(0, 0.10, 12.4)
1: panel.set_telemetry(0.75 + sin(elapsed) * 0.25, 0.11, 12.4)
2: panel.set_telemetry(248.6, 2.54, 7.4)
3: panel.set_telemetry(850, 8.6, 4.2)
4: panel.set_telemetry(400, 9.6, -1.5)
5: panel.set_telemetry(1010, 10.1, 3.2)
6, 7: pass
+1
View File
@@ -0,0 +1 @@
uid://5qywekq0w4ne
+67
View File
@@ -0,0 +1,67 @@
@tool
extends PanelContainer
@export var maximum_depth_m := 1000.0
@export var maximum_pressure_mpa := 10.0
@export var shallow_scale_knee_m := 1.0
@export_range(0, 1) var warning_fraction := 0.8
@export_range(0, 1) var fault_fraction := 0.95
@export var stale_after_seconds := 2.0
var _depth := 0.0
var _pressure := 0.0
var _temperature := 0.0
var _received_ms := -1
var _stale := false
func _ready() -> void:
_refresh()
## Depth in metres, pressure in MPa, water temperature in Celsius; supplied independently.
func set_telemetry(depth_m: float, pressure_mpa: float, water_temperature_c: float) -> bool:
if not is_finite(depth_m) or not is_finite(pressure_mpa) or not is_finite(water_temperature_c) or pressure_mpa < 0:
return false
_depth = depth_m
_pressure = pressure_mpa
_temperature = water_temperature_c
_received_ms = Time.get_ticks_msec()
_refresh()
return true
func clear_telemetry() -> void:
_received_ms = -1
_refresh()
func _severity(ratio: float) -> int:
return 2 if ratio >= maxf(warning_fraction, fault_fraction) else (1 if ratio >= warning_fraction else 0)
func _process(_delta: float) -> void:
var stale := _received_ms >= 0 and Time.get_ticks_msec() - _received_ms > stale_after_seconds * 1000
if stale != _stale:
_refresh()
func _refresh() -> void:
if not is_node_ready():
return
var measured := _received_ms >= 0
_stale = measured and Time.get_ticks_msec() - _received_ms > stale_after_seconds * 1000
var depth_ratio := _depth / maxf(maximum_depth_m, 0.001)
var pressure_ratio := _pressure / maxf(maximum_pressure_mpa, 0.001)
var worst_ratio := maxf(depth_ratio, pressure_ratio)
var variants := ["Label", "InstrumentWarning", "InstrumentFault"]
%DepthValue.text = "%.2f m" % _depth if measured else "— m"
%PressureValue.text = "%.2f MPa" % _pressure if measured else "— MPa"
%TemperatureValue.text = "%.1f °C" % _temperature if measured else "— °C"
%DepthReference.text = "/ %s m" % String.num(maximum_depth_m, 2).trim_suffix(".0")
%PressureReference.text = "/ %s MPa" % String.num(maximum_pressure_mpa, 2).trim_suffix(".0")
%DepthTitle.theme_type_variation = variants[_severity(depth_ratio)] if measured else "Label"
%PressureTitle.theme_type_variation = variants[_severity(pressure_ratio)] if measured else "Label"
%State.text = "NO TELEMETRY" if not measured else ("STALE" if _stale else ("OVER LIMIT" if worst_ratio > 1 else ("NEAR LIMIT" if _severity(worst_ratio) > 0 else "")))
%State.theme_type_variation = "InstrumentMuted" if not measured else ("InstrumentWarning" if _stale else variants[_severity(worst_ratio)])
%DepthScale.depth_m = _depth
%DepthScale.maximum_depth_m = maxf(maximum_depth_m, 0.001)
%DepthScale.knee_m = maxf(shallow_scale_knee_m, 0.001)
%DepthScale.warning_fraction = warning_fraction
%DepthScale.fault_fraction = maxf(warning_fraction, fault_fraction)
%DepthScale.available = measured
%DepthScale.queue_redraw()
+1
View File
@@ -0,0 +1 @@
uid://dnafkcowopwod
+1 -1
View File
@@ -5,7 +5,7 @@ const ENTRIES = {
"camera": {"title": "Camera", "description": "16:9 video surface with a stream header.", "scene": preload("res://components/camera_layout_slot.tscn"), "size": Vector2i(16, 10), "minimum": Vector2i(8, 5), "reference": Vector2(960, 600)},
"vehicle": {"title": "Vehicle status", "description": "Mode, faults, electrical readings and internal temperature.", "scene": preload("res://components/vehicle_status_panel.tscn"), "size": Vector2i(10, 8), "minimum": Vector2i(6, 4), "reference": Vector2(600, 440)},
"motion": {"title": "Motion", "description": "Attitude, heading, acceleration and angular momentum.", "scene": preload("res://components/motion_panel.tscn"), "size": Vector2i(10, 7), "minimum": Vector2i(6, 4), "reference": Vector2(600, 410), "background": NodePath("Content/Background")},
"environment": {"title": "Environment", "description": "Reserved for depth, pressure and water temperature.", "scene": preload("res://components/layout_slot.tscn"), "size": Vector2i(6, 5), "minimum": Vector2i(4, 3), "reference": Vector2(360, 240), "fill": true},
"environment": {"title": "Environment", "description": "Depth with a shallow-expanded scale, outside pressure and water temperature.", "scene": preload("res://components/environment_panel.tscn"), "size": Vector2i(6, 5), "minimum": Vector2i(4, 3), "reference": Vector2(360, 240)},
"link": {"title": "Link", "description": "Connection state, traffic utilization and packet loss.", "scene": preload("res://components/link_panel.tscn"), "size": Vector2i(6, 4), "minimum": Vector2i(4, 3), "reference": Vector2(360, 240)},
"videovr": {"title": "Video / VR", "description": "Reserved for latency, headset connection and resolution.", "scene": preload("res://components/layout_slot.tscn"), "size": Vector2i(12, 8), "minimum": Vector2i(6, 4), "reference": Vector2(600, 400), "fill": true},
"operatortools": {"title": "Operator tools", "description": "Reserved for recording, manipulator controls and terminal.", "scene": preload("res://components/layout_slot.tscn"), "size": Vector2i(10, 6), "minimum": Vector2i(6, 4), "reference": Vector2(600, 400), "fill": true},