Added motion planel for raw/pitch/roll and acceleration. Consolidated all colors into one stylesheet in themes/palette.gd.

This commit is contained in:
RedNicStone committed 2026-11-07 21:31:14 +01:00
1 parent 948b177eee
commit f05a6148fb
13 files changed
+672

No files matched your search

+332
View File
@@ -0,0 +1,332 @@
[gd_scene format=3 uid="uid://dvljqi38sjimi"]
[ext_resource type="Script" uid="uid://dh2vnk35potq3" path="res://scripts/motion_panel.gd" id="1"]
[ext_resource type="Script" uid="uid://1dn362pyibkx" path="res://scripts/motion_instrument.gd" id="2"]
[node name="MotionPanel" type="Control" unique_id=660733185]
custom_minimum_size = Vector2(600, 410)
custom_maximum_size = Vector2(600, 410)
layout_mode = 3
anchors_preset = 0
offset_right = 600.0
offset_bottom = 440.0
tooltip_text = "Orientation: roll right side down, pitch nose up, heading clockwise from north.
Body axes: X forward, Y right, Z down. Acceleration excludes gravity."
script = ExtResource("1")
[node name="Background" type="Panel" parent="." unique_id=144412302]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_type_variation = &"MotionBackground"
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=246886683]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 16.0
offset_top = 8.0
offset_right = -16.0
offset_bottom = -8.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 4
[node name="GridContainer" type="GridContainer" parent="VBoxContainer" unique_id=682224599]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/h_separation = 8
columns = 3
[node name="Attitude" type="VBoxContainer" parent="VBoxContainer/GridContainer" unique_id=200553262]
custom_minimum_size = Vector2(244, 0)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 2
[node name="MarginContainer" type="HBoxContainer" parent="VBoxContainer/GridContainer/Attitude" unique_id=1592037194]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/GridContainer/Attitude/MarginContainer" unique_id=678832939]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "ATTITUDE deg"
[node name="DialAspect" type="MarginContainer" parent="VBoxContainer/GridContainer/Attitude" unique_id=1404917279]
custom_minimum_size = Vector2(0, 264)
layout_mode = 2
size_flags_vertical = 0
[node name="Horizon" type="Control" parent="VBoxContainer/GridContainer/Attitude/DialAspect" unique_id=1698568859]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
mouse_filter = 2
script = ExtResource("2")
[node name="OrientationReadouts" type="HBoxContainer" parent="VBoxContainer/GridContainer/Attitude" unique_id=185739847]
layout_mode = 2
theme_override_constants/separation = 12
[node name="Roll" type="Label" parent="VBoxContainer/GridContainer/Attitude/OrientationReadouts" unique_id=818199825]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 19
text = "R —°"
[node name="Pitch" type="Label" parent="VBoxContainer/GridContainer/Attitude/OrientationReadouts" unique_id=25151996]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 19
text = "P —°"
horizontal_alignment = 2
[node name="HeadingTape" type="Control" parent="VBoxContainer/GridContainer/Attitude" unique_id=736154972]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 40)
layout_mode = 2
mouse_filter = 2
script = ExtResource("2")
instrument = 1
[node name="Heading" type="Label" parent="VBoxContainer/GridContainer/Attitude" unique_id=1630510362]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "H —"
horizontal_alignment = 1
[node name="CategorySeparator" type="VSeparator" parent="VBoxContainer/GridContainer" unique_id=1838523344]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/separation = 1
theme_type_variation = &"MotionVSeparator"
[node name="Acceleration" type="VBoxContainer" parent="VBoxContainer/GridContainer" unique_id=1201786358]
custom_minimum_size = Vector2(307, 0)
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/separation = 2
[node name="MarginContainer" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration" unique_id=1329156619]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/GridContainer/Acceleration/MarginContainer" unique_id=2085073917]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 18
text = "ACCEL m/s²"
[node name="FullScale" type="Label" parent="VBoxContainer/GridContainer/Acceleration/MarginContainer" unique_id=248494772]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "±5.0"
[node name="Limit" type="Label" parent="VBoxContainer/GridContainer/Acceleration/MarginContainer" unique_id=1467570655]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme_type_variation = &"MotionWarning"
theme_override_font_sizes/font_size = 18
text = "LIMIT"
[node name="Plots" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration" unique_id=127821710]
custom_minimum_size = Vector2(0, 264)
layout_mode = 2
size_flags_vertical = 0
theme_override_constants/separation = 4
[node name="AccelerationPlot" type="Control" parent="VBoxContainer/GridContainer/Acceleration/Plots" unique_id=383346065 node_paths=PackedStringArray("radius_source")]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
mouse_filter = 2
script = ExtResource("2")
instrument = 2
radius_source = NodePath("../../../Attitude/DialAspect/Horizon")
[node name="VerticalAxis" type="Control" parent="VBoxContainer/GridContainer/Acceleration/Plots" unique_id=1631611633]
custom_minimum_size = Vector2(24, 0)
layout_mode = 2
[node name="Axis" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Plots/VerticalAxis" unique_id=1652971232]
layout_mode = 0
offset_right = 24.0
offset_bottom = 26.0
theme_override_font_sizes/font_size = 18
text = "−Z"
horizontal_alignment = 1
[node name="VerticalAcceleration" type="Control" parent="VBoxContainer/GridContainer/Acceleration/Plots/VerticalAxis" unique_id=786328417 node_paths=PackedStringArray("radius_source")]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
mouse_filter = 2
script = ExtResource("2")
instrument = 3
radius_source = NodePath("../../AccelerationPlot")
[node name="Readouts" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration" unique_id=1041397004]
custom_minimum_size = Vector2(260, 0)
layout_mode = 2
size_flags_horizontal = 4
theme_override_constants/separation = 8
[node name="X" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration/Readouts" unique_id=1850756267]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 4
[node name="AxisX" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Readouts/X" unique_id=1279485247]
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "X"
[node name="AccelX" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Readouts/X" unique_id=528426129]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "—"
[node name="Y" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration/Readouts" unique_id=1540446279]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 4
[node name="AxisY" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Readouts/Y" unique_id=1789229948]
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "Y"
[node name="AccelY" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Readouts/Y" unique_id=1811878137]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "—"
[node name="Z" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration/Readouts" unique_id=449449369]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 4
[node name="AxisZ" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Readouts/Z" unique_id=1812289041]
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "Z"
[node name="AccelZ" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Readouts/Z" unique_id=648512841]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "—"
[node name="MomentumSeparator" type="HSeparator" parent="VBoxContainer/GridContainer/Acceleration" unique_id=498814282]
custom_minimum_size = Vector2(0, 12)
layout_mode = 2
theme_override_constants/separation = 4
theme_type_variation = &"MotionHSeparator"
[node name="MomentumTitle" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration" unique_id=1176164218]
layout_mode = 2
theme_override_constants/separation = 8
[node name="Label" type="Label" parent="VBoxContainer/GridContainer/Acceleration/MomentumTitle" unique_id=1226461986]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "ANG. MOMENTUM"
[node name="Units" type="Label" parent="VBoxContainer/GridContainer/Acceleration/MomentumTitle" unique_id=1830199918]
layout_mode = 2
theme_override_font_sizes/font_size = 18
text = "kg·m²/s"
[node name="Momentum" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration" unique_id=276201290]
custom_minimum_size = Vector2(260, 0)
layout_mode = 2
size_flags_horizontal = 4
theme_override_constants/separation = 8
[node name="X" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration/Momentum" unique_id=166270987]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 4
[node name="Axis" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Momentum/X" unique_id=243294341]
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "X"
[node name="MomentumX" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Momentum/X" unique_id=210690849]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "—"
[node name="Y" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration/Momentum" unique_id=722649907]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 4
[node name="Axis" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Momentum/Y" unique_id=1590278938]
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "Y"
[node name="MomentumY" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Momentum/Y" unique_id=349989084]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "—"
[node name="Z" type="HBoxContainer" parent="VBoxContainer/GridContainer/Acceleration/Momentum" unique_id=517849282]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 4
[node name="Axis" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Momentum/Z" unique_id=1648789922]
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "Z"
[node name="MomentumZ" type="Label" parent="VBoxContainer/GridContainer/Acceleration/Momentum/Z" unique_id=1970978687]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 19
text = "—"
[node name="StatusBanner" type="PanelContainer" parent="." unique_id=1814858071]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 44.0
grow_horizontal = 2
mouse_filter = 2
theme_type_variation = &"MotionAlertPanel"
[node name="Status" type="Label" parent="StatusBanner" unique_id=1561328163]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"MotionAlertText"
theme_override_font_sizes/font_size = 22
text = "NO DATA"
horizontal_alignment = 1
+13
View File
@@ -0,0 +1,13 @@
# Colors
Edit `themes/palette.gd`: base swatches define the hues; derived shades use `lightened()`, `darkened()`, and `lerp()`. `SOFT`, `HIGH_CONTRAST`, and `INSTRUMENT` map them to theme roles. Treat derived static values as read-only.
Regenerate after palette edits:
```sh
godot --headless --path . --script themes/build_theme.gd
```
Commit the generated themes and compass material alongside palette changes. The generator also updates the viewport background when needed.
Controls inherit the active theme. Custom graphics use `get_theme_color(role, "SurfaceSide")` for instrument roles and `get_theme_color("font_color", "Label")` for text. Prefer existing roles before adding new ones.
+20
View File
@@ -0,0 +1,20 @@
# Motion instrument
Instance `components/motion_panel.tscn`. Run `scenes/motion_demo.tscn` with F6 for simulated telemetry; pause the feed to preview stale status.
```gdscript
$MotionPanel.set_telemetry(
Vector3(roll_degrees, pitch_degrees, heading_degrees),
Vector3(momentum_x, momentum_y, momentum_z),
Vector3(accel_x, accel_y, accel_z)
)
```
- Orientation: positive roll right side down, positive pitch nose up, heading clockwise from north (0–360°).
- Body axes: X forward, Y right, Z down.
- Angular momentum: kg·m²/s, distinct from gyroscope angular velocity.
- Acceleration: m/s², with gravity removed upstream.
Missing values show dashes; non-finite samples are rejected. After `stale_after_seconds` (default 2), the banner marks held values STALE. `acceleration_full_scale` sets plot limits; numeric values remain unclamped.
Edit layout and Labels in the scene tree. Preserve scene-unique names used by `motion_panel.gd`. The four graphics use `motion_instrument.gd`; `radius_source` keeps the circles and Z scale aligned. Colors inherit the active theme; see [colors.md](colors.md).
+22
View File
@@ -0,0 +1,22 @@
[gd_scene format=3 uid="uid://b8u5uwdoxxvb1"]
[ext_resource type="PackedScene" uid="uid://dvljqi38sjimi" path="res://components/motion_panel.tscn" id="1"]
[ext_resource type="Script" uid="uid://c5mncovlnarve" path="res://scripts/motion_demo.gd" id="2"]
[node name="MotionDemo" type="VBoxContainer" unique_id=1608361665]
offset_left = 24.0
offset_top = 24.0
offset_right = 624.0
offset_bottom = 530.0
script = ExtResource("2")
[node name="DemoNotice" type="Label" parent="." unique_id=937059766]
layout_mode = 2
text = "SIMULATED TELEMETRY / motion instrument preview"
[node name="MotionPanel" parent="." unique_id=843107870 instance=ExtResource("1")]
layout_mode = 2
[node name="Pause" type="CheckButton" parent="." unique_id=103027158]
layout_mode = 2
text = "Pause feed (shows stale state after 2 seconds)"
+18
View File
@@ -0,0 +1,18 @@
extends VBoxContainer
var elapsed := 0.0
var running := true
@onready var instrument = $MotionPanel
func _ready() -> void:
$Pause.toggled.connect(func(paused: bool) -> void: running = not paused)
func _process(delta: float) -> void:
if not running:
return
elapsed += delta
instrument.set_telemetry(
Vector3(24 * sin(elapsed * 0.5), 18 * sin(elapsed * 0.3), fposmod(350 + elapsed * 4, 360)),
Vector3(1.8 * cos(elapsed * 0.5), 0.9 * cos(elapsed * 0.3), 0.6),
Vector3(2.8 * sin(elapsed * 0.7), 1.9 * cos(elapsed * 0.5), 1.2 * sin(elapsed * 0.4))
)
+1
View File
@@ -0,0 +1 @@
uid://c5mncovlnarve
+133
View File
@@ -0,0 +1,133 @@
@tool
extends Control
@export_enum("Attitude", "Heading", "Acceleration", "Vertical acceleration") var instrument: int = 0
## Shares the circle radius with another instrument.
@export var radius_source: Control
var attitude := Vector3.ZERO
var acceleration := Vector3.ZERO
var available := false
var full_scale := 5.0
func _ready() -> void:
resized.connect(queue_redraw)
theme_changed.connect(queue_redraw)
if is_instance_valid(radius_source):
radius_source.resized.connect(queue_redraw)
func _text(at: Vector2, value: String, font_size: int = 16, color: Variant = null) -> void:
draw_string(get_theme_default_font(), at, value, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, _ink() if color == null else color)
func _line(a: Vector2, b: Vector2, color: Variant = null, width: float = 1.0) -> void:
draw_line(a, b, _role("instrument_grid") if color == null else color, width, true)
func _draw() -> void:
match instrument:
0: _draw_attitude(available)
1: _draw_heading()
2: _draw_acceleration()
3: _draw_vertical()
func _horizon_half(center: Vector2, radius: float, normal: Vector2, displacement: float) -> PackedVector2Array:
var polygon := PackedVector2Array()
var previous := Vector2(radius, 0)
var previous_distance := previous.dot(normal) - displacement
for step in range(1, 129):
var point := Vector2.from_angle(step * TAU / 128.0) * radius
var distance := point.dot(normal) - displacement
if (distance >= 0) != (previous_distance >= 0):
polygon.append(center + previous.lerp(point, previous_distance / (previous_distance - distance)))
if distance >= 0:
polygon.append(center + point)
previous = point
previous_distance = distance
return polygon
func _draw_attitude(available: bool) -> void:
var center := circle_center()
var radius := circle_radius()
draw_circle(center, radius, _role("horizon_sky") if available else _role("instrument_empty"))
if available:
var rotation := deg_to_rad(-attitude.x)
var normal := Vector2.DOWN.rotated(rotation)
var direction := Vector2.RIGHT.rotated(rotation)
var displacement := attitude.y * radius / 60.0
var ground := _horizon_half(center, radius, normal, displacement)
if ground.size() >= 3:
draw_colored_polygon(ground, _role("horizon_ground"))
if absf(displacement) < radius:
var half_chord := sqrt(radius * radius - displacement * displacement)
_line(center + normal * displacement - direction * half_chord, center + normal * displacement + direction * half_chord, _ink(), 3)
for pitch_mark in range(-90, 91, 10):
var y: float = (attitude.y - pitch_mark) * radius / 60.0
if pitch_mark != 0 and absf(y) < radius - 25:
var p := center + normal * y
var width := 26.0 if pitch_mark % 20 == 0 else 15.0
_line(p - direction * width, p + direction * width, _ink(), 2)
if pitch_mark % 20 == 0:
_text(p + direction * (width + 7) + Vector2(0, 6), str(absi(pitch_mark)), 18)
for bank in [-60, -30, 0, 30, 60]:
var radial := Vector2.UP.rotated(deg_to_rad(bank))
_line(center + radial * (radius - 10), center + radial * (radius - 1), _ink(), 2)
var pointer := Vector2.UP.rotated(rotation)
draw_colored_polygon(PackedVector2Array([center + pointer * (radius - 17), center + pointer.rotated(0.07) * (radius - 29), center + pointer.rotated(-0.07) * (radius - 29)]), _role("instrument_accent"))
draw_arc(center, radius, 0, TAU, 128, _role("instrument_grid"), 2, true)
_line(center + Vector2(-47, 0), center + Vector2(-14, 0), _role("instrument_accent"), 4)
_line(center + Vector2(14, 0), center + Vector2(47, 0), _role("instrument_accent"), 4)
_line(center + Vector2(-14, 0), center + Vector2(-14, 8), _role("instrument_accent"), 3)
_line(center + Vector2(14, 0), center + Vector2(14, 8), _role("instrument_accent"), 3)
draw_circle(center, 4, _role("instrument_accent"))
func _heading_tick_x(degrees: float) -> float:
return size.x / 2 + (degrees - attitude.z) * size.x / 100.0
func _draw_heading() -> void:
if available:
var first_tick := int(floor((attitude.z - 50.0) / 10.0)) * 10
for degrees in range(first_tick, first_tick + 120, 10):
var x := _heading_tick_x(degrees)
if x < 18 or x > size.x - 18:
continue
_line(Vector2(x, 7), Vector2(x, 16), _ink(), 2)
if degrees % 20 == 0:
_text(Vector2(x - 17, 36), "%03d" % int(fposmod(degrees, 360)), 18, _ink())
_line(Vector2(size.x / 2, 0), Vector2(size.x / 2, 20), _role("instrument_accent"), 3)
func _draw_acceleration() -> void:
var radius := circle_radius()
var center := circle_center()
for fraction in [0.5, 1.0]:
draw_arc(center, radius * fraction, 0, TAU, 96, _role("instrument_grid"), 2, true)
_line(center - Vector2(radius, 0), center + Vector2(radius, 0), _role("instrument_axis"))
_line(center - Vector2(0, radius), center + Vector2(0, radius), _role("instrument_axis"))
_text(center + Vector2(-14, -radius - 8), "+X", 18)
_text(center + Vector2(radius + 2, 6), "+Y", 18)
if available:
var planar := Vector2(acceleration.y, -acceleration.x) / maxf(full_scale, 0.1)
var endpoint := center + planar.limit_length(1.0) * radius
_line(center, endpoint, _role("instrument_accent"), 4)
draw_circle(endpoint, 7, _role("instrument_accent"))
func _draw_vertical() -> void:
var center := circle_center()
var radius := circle_radius()
_line(center - Vector2(0, radius), center + Vector2(0, radius), _role("instrument_axis"), 2)
for fraction in [-1.0, -0.5, 0.0, 0.5, 1.0]:
var y: float = center.y + fraction * radius
_line(Vector2(center.x - 6, y), Vector2(center.x + 6, y), _ink(), 2)
if available:
var y := center.y + clampf(acceleration.z / maxf(full_scale, 0.1), -1, 1) * radius
_line(Vector2(center.x - 10, y), Vector2(center.x + 10, y), _role("instrument_accent"), 5)
func circle_radius() -> float:
if is_instance_valid(radius_source):
return radius_source.circle_radius()
return maxf(1.0, minf(size.x - 24, size.y - 44) / 2)
func circle_center() -> Vector2:
return size / 2 + Vector2(0, 10)
func _ink() -> Color:
return get_theme_color("font_color", "Label")
func _role(name: StringName) -> Color:
return get_theme_color(name, "SurfaceSide")
+1
View File
@@ -0,0 +1 @@
uid://1dn362pyibkx
+63
View File
@@ -0,0 +1,63 @@
@tool
extends Control
## Telemetry conventions: docs/motion_panel.md.
@export var stale_after_seconds: float = 2.0
@export_range(0.1, 100.0) var acceleration_full_scale: float = 5.0:
set(value):
acceleration_full_scale = maxf(value, 0.1)
_refresh()
var attitude := Vector3.ZERO # roll, pitch, heading in degrees
var momentum := Vector3.ZERO
var acceleration := Vector3.ZERO
var _received_ms: int = -1
var _last_stale := false
func _ready() -> void:
_refresh()
func set_telemetry(orientation_degrees: Vector3, angular_momentum: Vector3, linear_acceleration: Vector3) -> void:
if not orientation_degrees.is_finite() or not angular_momentum.is_finite() or not linear_acceleration.is_finite():
return
attitude = Vector3(wrapf(orientation_degrees.x, -180, 180), wrapf(orientation_degrees.y, -180, 180), fposmod(orientation_degrees.z, 360))
momentum = angular_momentum
acceleration = linear_acceleration
_received_ms = Time.get_ticks_msec()
_refresh()
func _process(_delta: float) -> void:
var stale := _is_stale()
if stale != _last_stale:
_refresh()
func _is_stale() -> bool:
return _received_ms >= 0 and (Time.get_ticks_msec() - _received_ms) / 1000.0 > stale_after_seconds
func _number(value: float, precision: int = 2) -> String:
return "—" if _received_ms < 0 else ("%+.*f" % [precision, value])
func _refresh() -> void:
if not is_node_ready():
return
_last_stale = _is_stale()
%Status.text = "STALE" if _last_stale else "NO DATA"
%StatusBanner.visible = _last_stale or _received_ms < 0
%Status.visible = true
%Roll.text = "R " + _number(attitude.x, 1) + "°"
%Pitch.text = "P " + _number(attitude.y, 1) + "°"
%Heading.text = "H " + ("%05.1f°" % attitude.z if _received_ms >= 0 else "—")
var accel_labels := [%AccelX, %AccelY, %AccelZ]
var momentum_labels := [%MomentumX, %MomentumY, %MomentumZ]
for axis in range(3):
accel_labels[axis].text = _number(acceleration[axis])
momentum_labels[axis].text = _number(momentum[axis])
%FullScale.text = "±%.1f" % acceleration_full_scale
%Limit.visible = _received_ms >= 0 and (Vector2(acceleration.x, acceleration.y).length() > acceleration_full_scale or absf(acceleration.z) > acceleration_full_scale)
for graphic in [%Horizon, %HeadingTape, %AccelerationPlot, %VerticalAcceleration]:
graphic.attitude = attitude
graphic.acceleration = acceleration
graphic.available = _received_ms >= 0
graphic.full_scale = acceleration_full_scale
graphic.queue_redraw()
+1
View File
@@ -0,0 +1 @@
uid://dh2vnk35potq3
+10
View File
@@ -0,0 +1,10 @@
[gd_resource type="StandardMaterial3D" format=3]
[ext_resource type="Texture2D" path="res://assets/vector/dial.svg" id="1_7hb6v"]
[resource]
transparency = 1
cull_mode = 2
shading_mode = 0
albedo_color = Color(0.44361365, 0.72118443, 0, 1)
albedo_texture = ExtResource("1_7hb6v")
+57
View File
@@ -0,0 +1,57 @@
extends RefCounted
## Rebuild: godot --headless --path . --script themes/build_theme.gd
const ACCENT = Color("ff7b00")
const SECONDARY = Color("402e32")
const TEXT = Color("f5f1ed")
const PANEL = Color("201b1f")
const SKY = Color("172f40")
const GROUND = Color("4b3930")
const COMPASS_TEST = Color(0.44361365, 0.72118443, 0, 1)
const WHITE = Color.WHITE
const CLEAR = Color.TRANSPARENT
# Color methods cannot initialize constants; treat these static values as read-only.
static var ACCENT_LIGHT: Color = ACCENT.lightened(0.25)
static var ACCENT_DARK: Color = ACCENT.darkened(0.25)
static var INPUT: Color = PANEL.darkened(0.40)
static var SURFACE: Color = PANEL.lightened(0.04)
static var PANEL_DIM: Color = PANEL.darkened(0.20)
static var TEXT_SOFT: Color = TEXT.darkened(0.055)
static var TEXT_MUTED: Color = TEXT.lerp(SECONDARY, 0.34)
static var TEXT_MUTED_SOFT: Color = TEXT.lerp(SECONDARY, 0.40)
static var BORDER: Color = TEXT.lerp(SECONDARY, 0.64)
static var BORDER_DISABLED: Color = SECONDARY.lightened(0.08)
static var GRID: Color = SECONDARY.lightened(0.18)
static var THUMB_SOFT: Color = TEXT.lerp(SECONDARY, 0.18)
static var VIEWPORT_BACKGROUND: Color = PANEL.darkened(0.45)
static var HIGH_CONTRAST: Dictionary = {
"text": TEXT, "muted_text": TEXT_MUTED,
"active_text": INPUT, "surface": PANEL,
"panel": PANEL, "input": INPUT,
"hover": SECONDARY, "pressed": ACCENT, "fill": ACCENT,
"disabled": PANEL_DIM, "border": BORDER,
"disabled_border": BORDER_DISABLED, "focus": ACCENT,
"separator": SECONDARY, "thumb": TEXT_MUTED,
"fill_text": INPUT,
}
static var SOFT: Dictionary = {
"text": TEXT_SOFT, "muted_text": TEXT_MUTED_SOFT,
"active_text": TEXT, "surface": SURFACE,
"panel": PANEL, "input": INPUT,
"hover": SECONDARY, "pressed": SECONDARY,
"fill": ACCENT, "disabled": PANEL,
"border": CLEAR, "disabled_border": CLEAR,
"focus": ACCENT, "separator": SECONDARY,
"thumb": THUMB_SOFT, "fill_text": INPUT,
}
static var INSTRUMENT: Dictionary = {
"instrument_accent": ACCENT_LIGHT,
"instrument_grid": GRID,
"horizon_sky": SKY,
"horizon_ground": GROUND,
"alert_background": ACCENT_DARK,
"alert_text": WHITE,
}
+1
View File
@@ -0,0 +1 @@
uid://deuuvf6y2c031