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

+136
View File
@@ -0,0 +1,136 @@
[gd_scene format=3 uid="uid://b75nt1kn2sobp"]
[ext_resource type="Script" uid="uid://dnafkcowopwod" path="res://scripts/environment_panel.gd" id="1"]
[ext_resource type="Script" uid="uid://cbobdfddbbddo" path="res://scripts/depth_scale.gd" id="2"]
[node name="EnvironmentPanel" type="PanelContainer" unique_id=1120688022]
custom_minimum_size = Vector2(360, 240)
offset_right = 360.0
offset_bottom = 240.0
theme_type_variation = &"VehicleBackground"
script = ExtResource("1")
[node name="Margin" type="MarginContainer" parent="." unique_id=1435834688]
layout_mode = 2
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 16
theme_override_constants/margin_bottom = 10
[node name="Body" type="VBoxContainer" parent="Margin" unique_id=826880208]
layout_mode = 2
theme_override_constants/separation = 6
[node name="Header" type="HBoxContainer" parent="Margin/Body" unique_id=1188782081]
layout_mode = 2
[node name="Title" type="Label" parent="Margin/Body/Header" unique_id=1435423541]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 18
text = "ENVIRONMENT"
[node name="State" type="Label" parent="Margin/Body/Header" unique_id=1387248588]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"InstrumentMuted"
theme_override_font_sizes/font_size = 14
text = "NO TELEMETRY"
horizontal_alignment = 2
[node name="Measurements" type="HBoxContainer" parent="Margin/Body" unique_id=354890970]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 16
[node name="Readouts" type="VBoxContainer" parent="Margin/Body/Measurements" unique_id=766009790]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 0
[node name="DepthTitle" type="Label" parent="Margin/Body/Measurements/Readouts" unique_id=1777992876]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"Label"
theme_override_font_sizes/font_size = 14
text = "DEPTH"
[node name="DepthValue" type="Label" parent="Margin/Body/Measurements/Readouts" unique_id=1937430512]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 40
text = "— m"
[node name="DepthReference" type="Label" parent="Margin/Body/Measurements/Readouts" unique_id=1946013161]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"InstrumentMuted"
theme_override_font_sizes/font_size = 14
text = "/ 1000 m"
[node name="Spacer" type="Control" parent="Margin/Body/Measurements/Readouts" unique_id=1016695424]
layout_mode = 2
size_flags_vertical = 3
[node name="Supporting" type="HBoxContainer" parent="Margin/Body/Measurements/Readouts" unique_id=1473880932]
layout_mode = 2
theme_override_constants/separation = 12
[node name="Pressure" type="VBoxContainer" parent="Margin/Body/Measurements/Readouts/Supporting" unique_id=1964987657]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 0
[node name="PressureTitle" type="Label" parent="Margin/Body/Measurements/Readouts/Supporting/Pressure" unique_id=1727076495]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"Label"
theme_override_font_sizes/font_size = 13
text = "PRESSURE"
[node name="PressureValue" type="Label" parent="Margin/Body/Measurements/Readouts/Supporting/Pressure" unique_id=1479127186]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 22
text = "— MPa"
[node name="PressureReference" type="Label" parent="Margin/Body/Measurements/Readouts/Supporting/Pressure" unique_id=1835332085]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"InstrumentMuted"
theme_override_font_sizes/font_size = 14
text = "/ 10 MPa"
[node name="Temperature" type="VBoxContainer" parent="Margin/Body/Measurements/Readouts/Supporting" unique_id=1827823179]
layout_mode = 2
theme_override_constants/separation = 0
[node name="Title" type="Label" parent="Margin/Body/Measurements/Readouts/Supporting/Temperature" unique_id=1829138035]
layout_mode = 2
theme_override_font_sizes/font_size = 13
text = "WATER TEMP"
[node name="TemperatureValue" type="Label" parent="Margin/Body/Measurements/Readouts/Supporting/Temperature" unique_id=1337672100]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 22
text = "— °C"
[node name="Scale" type="VBoxContainer" parent="Margin/Body/Measurements" unique_id=1235543623]
layout_mode = 2
theme_override_constants/separation = 0
[node name="DepthScale" type="Control" parent="Margin/Body/Measurements/Scale" unique_id=223719936]
unique_name_in_owner = true
custom_minimum_size = Vector2(74, 140)
layout_mode = 2
size_flags_vertical = 3
tooltip_text = "Shallow depths expanded: log(1 + depth / knee). Includes zero."
script = ExtResource("2")
[node name="Caption" type="Label" parent="Margin/Body/Measurements/Scale" unique_id=1491620694]
layout_mode = 2
theme_type_variation = &"InstrumentMuted"
theme_override_font_sizes/font_size = 12
text = "NONLINEAR"
horizontal_alignment = 1
-7
View File
@@ -1,20 +1,13 @@
To do:
- Pitch/yaw/tilt relative to magnetic north
- Motor information
- RPM
- Torque
- Power Consumption
- Overall power consumption
- Command line
- Inputs
- Camera Status
- VR Peripherals and ROV connection status
- Uplink/Downlink Data Rate
- Manipulator Pos/Rot
- 3D model of arm rotation
- Status
- Logging
- Enclosure Temperature
- Depth (Pressure)
- Tether Latency
+1 -1
View File
@@ -41,7 +41,7 @@ The elements array is the complete active layout; omitted default panels stay de
`layout_overview.tscn` owns the shader grid and toolbar. Each `layout_canvas.tscn` places its panel children using `layout_item.gd`; set their IDs, cell rectangles, and minimum sizes in the Inspector. Existing UI scenes remain under each item's `Content` node. Each allocation has a full-size panel background with a shared `panel_gap` (16 logical pixels by default). Instruments scale uniformly inside that frame; reserved slots fill it. Embedded component backgrounds are transparent so their scaling does not change the visible gutters. The camera retains its 16:9 picture inside the camera/header allocation.
The initial layouts deliberately leave free cells for rearranging. Environment, VR/video, and operator tools are placeholders. The Link panel has a separate [telemetry demo](link_panel.md). Camera and telemetry feeds are not connected.
The initial layouts deliberately leave free cells for rearranging. VR/video and operator tools are placeholders. The Link and Environment panels have separate [Link](link_panel.md) and [Environment](environment_panel.md) telemetry demos. Camera and telemetry feeds are not connected.
The library registry is `scripts/layout_catalog.gd`. Each entry supplies a scene, display text, preferred/minimum cells and sizing behavior. JSON stores registered type IDs rather than resource paths. `components/layout_library.tscn` owns the drawer; selection button order is unchanged.
+16
View File
@@ -0,0 +1,16 @@
# Environment panel
Run `scenes/environment_demo.tscn` with F6. It previews surface/zero, shallow operation, deeper readings, independent depth/pressure warnings, stale data and both themes.
```gdscript
$EnvironmentPanel.set_telemetry(248.6, 2.54, 7.4)
# depth: metres; outside pressure: MPa; water temperature: Celsius
```
The depth indicator uses `log(1 + depth / knee)`, normalized to the maximum depth. This includes zero and expands shallow readings; the default knee is 1 m. Tick labels show the non-linear spacing. Numeric readings are untransformed; negative depth remains visible numerically while the marker stays at zero. Above-limit readings remain visible while the marker clamps to the bottom.
Limits default to 1000 m and 10 MPa. Warning/saturation colors start at 80%/95% of each actual measurement's limit, independently of the scale transform. Temperature has no limit or gauge. Pressure and depth are supplied independently; no conversion is performed, and the pressure datum should match the configured pressure limit. Demo samples are illustrative.
Missing data shows dashes. After two seconds without a sample, values are marked STALE / HELD. Non-finite values and negative pressure are rejected. `clear_telemetry()` restores the missing-data state.
Labels and containers remain editable scene nodes; `depth_scale.gd` draws the indicator. There is no background icon. Colors use shared theme roles. The panel replaces the Environment placeholder in the library and both layouts without changing saved JSON type IDs.
+2 -1
View File
@@ -11,6 +11,7 @@ config_version=5
[application]
config/name="SurfaceSide-UI"
run/main_scene="res://scenes/layout_landscape.tscn"
config/features=PackedStringArray("4.7", "Forward Plus")
config/icon="res://icon.svg"
@@ -31,4 +32,4 @@ theme/custom="res://themes/theme.tres"
[rendering]
rendering_device/driver.windows="d3d12"
environment/defaults/default_clear_color=Color(0.067, 0.055, 0.067, 1)
environment/defaults/default_clear_color=Color(0.069019616, 0.058235295, 0.06686275, 1)
+2 -9
View File
@@ -1,19 +1,12 @@
[gd_scene format=3 uid="uid://j2vmxn1p5hej"]
[ext_resource type="Script" uid="uid://cgcgn8lwigumx" path="res://scripts/angle_display_3d.gd" id="1_8a3he"]
[ext_resource type="Texture2D" uid="uid://cjw3n47kehnfj" path="res://assets/vector/dial.svg" id="1_vggt3"]
[ext_resource type="Material" path="res://themes/compass_material.tres" id="1_material"]
[ext_resource type="PackedScene" uid="uid://0qb2f8igltax" path="res://assets/models/grey-seal-halichoerus-grypus/grey_seal_clean.glb" id="2_8a3he"]
[ext_resource type="Script" uid="uid://cv5cmdlub5gtx" path="res://scenes/node_3d.gd" id="3_mut2o"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_mut2o"]
transparency = 1
cull_mode = 2
shading_mode = 0
albedo_color = Color(0.44361365, 0.72118443, 0, 1)
albedo_texture = ExtResource("1_vggt3")
[sub_resource type="PlaneMesh" id="PlaneMesh_wivqi"]
material = SubResource("StandardMaterial3D_mut2o")
material = ExtResource("1_material")
[node name="Compass" type="Node3D" unique_id=1678590387]
+62
View File
@@ -0,0 +1,62 @@
[gd_scene format=3]
[ext_resource type="PackedScene" path="res://components/layout_canvas.tscn" id="canvas"]
[ext_resource type="PackedScene" path="res://components/layout_item.tscn" id="item"]
[ext_resource type="PackedScene" path="res://components/camera_layout_slot.tscn" id="camera"]
[ext_resource type="PackedScene" path="res://components/vehicle_status_panel.tscn" id="vehicle"]
[ext_resource type="PackedScene" path="res://components/motion_panel.tscn" id="motion"]
[ext_resource type="PackedScene" path="res://components/environment_panel.tscn" id="environment"]
[ext_resource type="PackedScene" path="res://components/link_panel.tscn" id="link"]
[ext_resource type="PackedScene" path="res://components/layout_slot.tscn" id="slot"]
[node name="DisplayLeft" instance=ExtResource("canvas")]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
grid_size = Vector2i(18, 32)
config_path = "user://layouts/portrait_left.json"
legacy_config_path = "user://layout_portrait_left.cfg"
[node name="Camera" parent="." instance=ExtResource("item")]
layout_mode = 0
panel_id = "camera"
cell_rect = Rect2i(0, 0, 18, 11)
minimum_cells = Vector2i(8, 5)
reference_size = Vector2(960, 600)
[node name="Content" parent="Camera" instance=ExtResource("camera")]
layout_mode = 0
[node name="Vehicle" parent="." instance=ExtResource("item")]
layout_mode = 0
panel_id = "vehicle"
cell_rect = Rect2i(0, 11, 10, 8)
minimum_cells = Vector2i(6, 4)
reference_size = Vector2(600, 440)
[node name="Content" parent="Vehicle" instance=ExtResource("vehicle")]
layout_mode = 0
[node name="Motion" parent="." instance=ExtResource("item")]
layout_mode = 0
panel_id = "motion"
cell_rect = Rect2i(0, 19, 10, 7)
minimum_cells = Vector2i(6, 4)
reference_size = Vector2(600, 410)
background_path = NodePath("Content/Background")
[node name="Content" parent="Motion" instance=ExtResource("motion")]
layout_mode = 0
[node name="Environment" parent="." instance=ExtResource("item")]
layout_mode = 0
panel_id = "environment"
cell_rect = Rect2i(10, 11, 8, 8)
minimum_cells = Vector2i(4, 3)
reference_size = Vector2(360, 240)
[node name="Content" parent="Environment" instance=ExtResource("environment")]
layout_mode = 0
+57
View File
@@ -0,0 +1,57 @@
[gd_scene format=3]
[ext_resource type="PackedScene" path="res://components/layout_canvas.tscn" id="canvas"]
[ext_resource type="PackedScene" path="res://components/layout_item.tscn" id="item"]
[ext_resource type="PackedScene" path="res://components/camera_layout_slot.tscn" id="camera"]
[ext_resource type="PackedScene" path="res://components/vehicle_status_panel.tscn" id="vehicle"]
[ext_resource type="PackedScene" path="res://components/motion_panel.tscn" id="motion"]
[ext_resource type="PackedScene" path="res://components/environment_panel.tscn" id="environment"]
[ext_resource type="PackedScene" path="res://components/link_panel.tscn" id="link"]
[ext_resource type="PackedScene" path="res://components/layout_slot.tscn" id="slot"]
[node name="DisplayRight" instance=ExtResource("canvas")]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
grid_size = Vector2i(18, 32)
config_path = "user://layouts/portrait_right.json"
legacy_config_path = "user://layout_portrait_right.cfg"
[node name="VideoVR" parent="." instance=ExtResource("item")]
layout_mode = 0
panel_id = "videovr"
cell_rect = Rect2i(0, 0, 18, 17)
minimum_cells = Vector2i(6, 4)
reference_size = Vector2(600, 400)
fill_allocation = true
[node name="Content" parent="VideoVR" instance=ExtResource("slot")]
layout_mode = 0
title = "VIDEO / VR"
description = "Latency history and breakdown · headset connection · device · resolution"
[node name="Link" parent="." instance=ExtResource("item")]
layout_mode = 0
panel_id = "link"
cell_rect = Rect2i(0, 17, 18, 5)
minimum_cells = Vector2i(4, 3)
reference_size = Vector2(360, 240)
[node name="Content" parent="Link" instance=ExtResource("link")]
layout_mode = 0
[node name="OperatorTools" parent="." instance=ExtResource("item")]
layout_mode = 0
panel_id = "operatortools"
cell_rect = Rect2i(0, 22, 18, 10)
minimum_cells = Vector2i(6, 4)
reference_size = Vector2(600, 400)
fill_allocation = true
[node name="Content" parent="OperatorTools" instance=ExtResource("slot")]
layout_mode = 0
title = "OPERATOR TOOLS / RESERVED"
description = "Recording · manipulator · terminal / events"
+52
View File
@@ -0,0 +1,52 @@
[gd_scene format=3]
[ext_resource type="Script" path="res://scripts/environment_demo.gd" id="1"]
[ext_resource type="PackedScene" path="res://components/environment_panel.tscn" id="2"]
[node name="EnvironmentDemo" type="VBoxContainer"]
offset_left = 24.0
offset_top = 24.0
offset_right = 624.0
offset_bottom = 420.0
theme_override_constants/separation = 16
script = ExtResource("1")
[node name="Notice" type="Label" parent="."]
layout_mode = 2
text = "SIMULATED / ENVIRONMENT INSTRUMENT"
[node name="EnvironmentPanel" parent="." instance=ExtResource("2")]
layout_mode = 2
size_flags_horizontal = 4
[node name="Controls" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="State" type="OptionButton" parent="Controls"]
layout_mode = 2
selected = 1
item_count = 8
popup/item_0/text = "Surface / zero"
popup/item_0/id = 0
popup/item_1/text = "Shallow operation"
popup/item_1/id = 1
popup/item_2/text = "Operating depth"
popup/item_2/id = 2
popup/item_3/text = "Near depth limit"
popup/item_3/id = 3
popup/item_4/text = "Pressure near limit"
popup/item_4/id = 4
popup/item_5/text = "Over limits"
popup/item_5/id = 5
popup/item_6/text = "Stop feed / stale"
popup/item_6/id = 6
popup/item_7/text = "No telemetry"
popup/item_7/id = 7
[node name="Contrast" type="CheckButton" parent="Controls"]
layout_mode = 2
text = "High contrast"
[node name="Note" type="Label" parent="."]
layout_mode = 2
text = "Zero-inclusive log scale · shallow depths expanded"
+3 -4
View File
@@ -11,6 +11,8 @@
[ext_resource type="PackedScene" path="res://components/link_panel.tscn" id="link"]
[ext_resource type="PackedScene" path="res://components/environment_panel.tscn" id="environment"]
[node name="LandscapeLayout" type="Control"]
layout_mode = 1
anchors_preset = 15
@@ -76,12 +78,9 @@ panel_id = "environment"
cell_rect = Rect2i(26, 0, 6, 8)
minimum_cells = Vector2i(4, 3)
reference_size = Vector2(360, 240)
fill_allocation = true
[node name="Content" parent="Overview/Dashboard/Canvas/Environment" instance=ExtResource("slot")]
[node name="Content" parent="Overview/Dashboard/Canvas/Environment" instance=ExtResource("environment")]
layout_mode = 0
title = "ENVIRONMENT"
description = "Depth · pressure · water temperature"
[node name="VideoVR" parent="Overview/Dashboard/Canvas" instance=ExtResource("item")]
layout_mode = 0
+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},
+86 -34
View File
@@ -1,39 +1,36 @@
extends SceneTree
const ACCENT = Color("ff7b00")
const SECONDARY = Color("402e32")
const CLEAR = Color.TRANSPARENT
const HIGH_CONTRAST = {
"text": Color("f5f1ed"), "muted_text": Color("b9b0b3"),
"active_text": Color("141114"), "surface": Color("201b1f"),
"panel": Color("201b1f"), "input": Color("141114"),
"hover": SECONDARY, "pressed": ACCENT, "fill": ACCENT,
"disabled": Color("191619"), "border": Color("817278"),
"disabled_border": Color("51464c"), "focus": ACCENT,
"separator": SECONDARY, "thumb": Color("b9b0b3"),
"fill_text": Color("141114"),
}
const SOFT = {
"text": Color("e7e0dd"), "muted_text": Color("ada2a8"),
"active_text": Color("f5f1ed"), "surface": Color("292126"),
"panel": Color("201b1f"), "input": Color("141114"),
"hover": SECONDARY, "pressed": SECONDARY,
"fill": ACCENT, "disabled": Color("211c20"),
"border": CLEAR, "disabled_border": CLEAR,
"focus": ACCENT, "separator": SECONDARY,
"thumb": Color("d2c6c9"), "fill_text": Color("141114"),
}
const COLORS = preload("res://themes/palette.gd")
func _initialize() -> void:
for high_contrast in [false, true]:
var palette: Dictionary = HIGH_CONTRAST if high_contrast else SOFT
var palette: Dictionary = COLORS.HIGH_CONTRAST if high_contrast else COLORS.SOFT
var path := "res://themes/theme_highcontrast.tres" if high_contrast else "res://themes/theme.tres"
var error := ResourceSaver.save(_build_theme(palette, high_contrast), path)
if error != OK:
push_error("Could not save %s: %s" % [path, error_string(error)])
quit(error)
return
var material := StandardMaterial3D.new()
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
material.cull_mode = BaseMaterial3D.CULL_DISABLED
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
material.albedo_color = COLORS.COMPASS_TEST
material.albedo_texture = load("res://assets/vector/dial.svg")
var material_error := ResourceSaver.save(material, "res://themes/compass_material.tres")
if material_error != OK:
push_error("Could not save compass material: " + error_string(material_error))
quit(material_error)
return
var config := ConfigFile.new()
var config_error := config.load("res://project.godot")
if config_error == OK and config.get_value("rendering", "environment/defaults/default_clear_color") != COLORS.VIEWPORT_BACKGROUND:
config.set_value("rendering", "environment/defaults/default_clear_color", COLORS.VIEWPORT_BACKGROUND)
config_error = config.save("res://project.godot")
if config_error != OK:
push_error("Could not update viewport background: " + error_string(config_error))
quit(config_error)
return
quit()
func _build_theme(palette: Dictionary, high_contrast: bool) -> Theme:
@@ -48,7 +45,7 @@ func _build_theme(palette: Dictionary, high_contrast: bool) -> Theme:
if "disabled" in name or "placeholder" in name or "readonly" in name:
color = palette.muted_text
elif "selection" in name:
color = SECONDARY
color = COLORS.SECONDARY
elif name in ["font_pressed_color", "font_hover_pressed_color"] and type in ["Button", "OptionButton", "MenuButton"]:
color = palette.active_text
elif name == "font_selected_color" and type == "TabContainer":
@@ -56,11 +53,11 @@ func _build_theme(palette: Dictionary, high_contrast: bool) -> Theme:
elif name == "font_color" and type == "ProgressBar":
color = palette.fill_text
elif "background" in name:
color = CLEAR
color = COLORS.CLEAR
elif "caret" in name or "accent" in name:
color = ACCENT
color = COLORS.ACCENT
elif "shadow" in name or "outline" in name:
color = CLEAR
color = COLORS.CLEAR
theme.set_color(name, type, color)
for name in theme.get_stylebox_list(type):
var original := theme.get_stylebox(name, type)
@@ -99,13 +96,11 @@ func _build_theme(palette: Dictionary, high_contrast: bool) -> Theme:
style.bg_color = palette.separator
style.border_color = palette.separator
theme.set_stylebox(name, type, style)
# Slider thumbs use square textures instead of the engine's round icons.
for type in ["HSlider", "VSlider"]:
for name in ["grabber", "grabber_highlight", "grabber_disabled"]:
var bitmap := Image.create(14, 18, false, Image.FORMAT_RGBA8)
bitmap.fill(ACCENT if name == "grabber_highlight" else palette.thumb)
bitmap.fill(COLORS.ACCENT if name == "grabber_highlight" else palette.thumb)
theme.set_icon(name, type, ImageTexture.create_from_image(bitmap))
# Dedicated panel states used by the keyboard focus containers.
var panel := theme.get_stylebox("panel", "PanelContainer").duplicate() as StyleBoxFlat
theme.set_stylebox("panel_inactive", "SurfaceSide", panel)
var active := panel.duplicate() as StyleBoxFlat
@@ -114,6 +109,63 @@ func _build_theme(palette: Dictionary, high_contrast: bool) -> Theme:
if not high_contrast:
active.border_width_top = 2
theme.set_stylebox("panel_active", "SurfaceSide", active)
theme.set_color("accent", "SurfaceSide", ACCENT)
theme.set_color("secondary", "SurfaceSide", SECONDARY)
theme.set_color("accent", "SurfaceSide", COLORS.ACCENT)
theme.set_color("secondary", "SurfaceSide", COLORS.SECONDARY)
_add_instrument_theme(theme, palette)
for role in COLORS.STATUS:
theme.set_color(role, "SurfaceSide", COLORS.STATUS[role])
theme.set_type_variation("VehicleWarningText", "Label")
theme.set_color("font_color", "VehicleWarningText", COLORS.STATUS.status_ink)
theme.set_type_variation("VehicleFaultText", "Label")
theme.set_color("font_color", "VehicleFaultText", COLORS.WHITE)
theme.set_type_variation("VehicleBackground", "PanelContainer")
var vehicle_background := StyleBoxFlat.new()
vehicle_background.bg_color = palette.panel
vehicle_background.border_color = palette.border
vehicle_background.set_border_width_all(1 if high_contrast else 0)
theme.set_stylebox("panel", "VehicleBackground", vehicle_background)
theme.set_type_variation("LayoutItemBackground", "Panel")
theme.set_stylebox("panel", "LayoutItemBackground", vehicle_background)
theme.set_type_variation("LayoutLibraryList", "ItemList")
theme.set_color("guide_color", "LayoutLibraryList", COLORS.SECONDARY if high_contrast else COLORS.CLEAR)
theme.set_constant("v_separation", "LayoutLibraryList", 8)
for variant in ["LinkRateNormal", "LinkRateWarning", "LinkRateFault", "LinkMuted"]:
theme.set_type_variation(variant, "Label")
theme.set_color("font_color", "LinkRateNormal", COLORS.INSTRUMENT.instrument_accent)
theme.set_color("font_color", "LinkRateWarning", COLORS.WARNING)
theme.set_color("font_color", "LinkRateFault", COLORS.FAULT)
theme.set_color("font_color", "LinkMuted", palette.muted_text)
for variant in ["InstrumentWarning", "InstrumentFault", "InstrumentMuted"]:
theme.set_type_variation(variant, "Label")
theme.set_color("font_color", "InstrumentWarning", COLORS.WARNING)
theme.set_color("font_color", "InstrumentFault", COLORS.FAULT)
theme.set_color("font_color", "InstrumentMuted", palette.muted_text)
return theme
func _add_instrument_theme(theme: Theme, palette: Dictionary) -> void:
for role in COLORS.INSTRUMENT:
theme.set_color(role, "SurfaceSide", COLORS.INSTRUMENT[role])
theme.set_color("instrument_empty", "SurfaceSide", palette.input)
theme.set_color("instrument_axis", "SurfaceSide", palette.muted_text)
theme.set_type_variation("MotionBackground", "Panel")
var background := StyleBoxFlat.new()
background.bg_color = palette.panel
theme.set_stylebox("panel", "MotionBackground", background)
for vertical in [false, true]:
var type := "MotionVSeparator" if vertical else "MotionHSeparator"
theme.set_type_variation(type, "VSeparator" if vertical else "HSeparator")
var separator := StyleBoxLine.new()
separator.color = COLORS.INSTRUMENT.instrument_grid
separator.thickness = 1
separator.vertical = vertical
theme.set_stylebox("separator", type, separator)
theme.set_type_variation("MotionAlertPanel", "PanelContainer")
var alert := StyleBoxFlat.new()
alert.bg_color = COLORS.INSTRUMENT.alert_background
alert.content_margin_top = 7
alert.content_margin_bottom = 7
theme.set_stylebox("panel", "MotionAlertPanel", alert)
theme.set_type_variation("MotionAlertText", "Label")
theme.set_color("font_color", "MotionAlertText", COLORS.INSTRUMENT.alert_text)
theme.set_type_variation("MotionWarning", "Label")
theme.set_color("font_color", "MotionWarning", COLORS.INSTRUMENT.instrument_accent)
+11
View File
@@ -8,6 +8,8 @@ const PANEL = Color("201b1f")
const SKY = Color("172f40")
const GROUND = Color("4b3930")
const COMPASS_TEST = Color(0.44361365, 0.72118443, 0, 1)
const WARNING = Color("f5c542")
const FAULT = Color("dc414b")
const WHITE = Color.WHITE
const CLEAR = Color.TRANSPARENT
@@ -55,3 +57,12 @@ static var INSTRUMENT: Dictionary = {
"alert_background": ACCENT_DARK,
"alert_text": WHITE,
}
static var STATUS: Dictionary = {
"warning": WARNING,
"warning_alternate": WARNING.darkened(0.36),
"fault": FAULT,
"fault_alternate": FAULT.darkened(0.50),
"status_ink": INPUT,
"status_icon": ACCENT_LIGHT,
}
+1409 -803
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff