1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Bevy `Component` newtypes for the frame-tree-as-entities
//! infrastructure: per-frame translational / rotational /
//! angular-velocity state, frame-kind markers, and the bidirectional
//! handles linking body / source / planet entities to their frame
//! entities.
use *;
use DVec3;
// intra-doc-link resolution
use ;
// ── Frames-as-entities components ──
//
// Live on **frame entities** (not body or source entities) and carry
// the per-frame state described in the [Frame-Tree-ECS-Native wiki
// page][1] (Section 13 sequencing). The ECS hierarchy is the single
// source of truth for all frame-tree state — gravity, integration,
// frame-switch, and mission code via [`crate::frame_param`] all read
// from the ECS hierarchy directly via `ChildOf` / `Children`.
//
// Component split rationale: the three pieces of `RefFrameState` are
// independently mutated in practice. `FrameTransC` is rewritten by
// integration / source-position updates; `FrameRotC` is rewritten by
// planet-fixed rotation updates; `FrameAngVelC` is rewritten alongside
// `FrameRotC` for pfix frames but stays at zero for inertial / body
// frames. Splitting lets change-detection fire on the right writers
// only.
//
// [1]: https://github.com/simnaut/astrodyn/wiki/Frame-Tree-ECS-Native#13-migration-sequencing
/// Translational state (position + velocity) of a frame entity
/// relative to its parent frame entity. Mirrors
/// [`astrodyn::RefFrameTrans`]. Stored raw (`DVec3`) at the additive-
/// infrastructure stage; later PRs in the sequence may carry typed
/// `Position<P>` / `Velocity<P>` keyed off the parent frame entity's
/// marker. Issue #277.
/// Rotational state of a frame entity relative to its parent: the
/// left-transformation quaternion and the cached transformation matrix
/// `t_parent_this`. Mirrors the rotation portion of
/// [`astrodyn::RefFrameRot`] minus `ang_vel_this`, which lives in
/// [`FrameAngVelC`] for change-detection granularity. Issue #277.
/// Angular velocity of a frame entity relative to its parent, expressed
/// in this-frame coordinates (rad/s). Mirrors
/// [`astrodyn::RefFrameRot::ang_vel_this`]. Split from [`FrameRotC`] so
/// pfix-rotation systems that only rewrite angular velocity (or body
/// integration that only rewrites attitude) get fine-grained
/// change-detection. Issue #277.
;
// Marker components — mark the kind of frame an entity represents.
// Bevy idiom: query keying via `With<…>`. Replaces the runtime
// `RefFrameKind` enum on the arena side. Suffix `Marker` avoids
// colliding with `astrodyn`'s phantom-frame types (`BodyFrame`,
// `PlanetFixed`, `IntegrationFrame`, `RootInertial`).
/// Marker: this entity is a root or planet inertial frame. Issue #277.
;
/// Marker: this entity is a planet-fixed (pfix) frame, child of an
/// inertial frame, rotating with the planet. Issue #277.
;
/// Marker: this entity is a body's body-frame (composite_body in JEOD).
/// Issue #277.
;
/// Marker: this entity has been registered as an integration frame for
/// at least one body. Inserted (idempotently) by
/// `register_body_frames_system` when a body is spawned with this
/// frame as its integration frame; never removed. The marker has
/// **sticky** semantics — `frame_switch_system` reparents a body's
/// frame entity (via `commands.entity(...).insert(ChildOf(...))`)
/// when the body switches frames, but does not touch this marker,
/// because (a) one integration frame entity can serve many bodies
/// and tracking a "currently in use" predicate would require
/// ref-counting, and (b) downstream SystemParam consumers in later
/// PRs of the [Section 13 sequence][1] only need to know whether a
/// frame entity *can* serve as an integration frame, which the
/// registration-time signal answers correctly. The authoritative
/// "this body's integration frame is X" lookup is the body frame
/// entity's `ChildOf` parent. A frame entity may carry both
/// `InertialFrameMarker` and `IntegrationFrameMarker` simultaneously
/// — they describe orthogonal properties of the frame. Issue #277.
///
/// [1]: https://github.com/simnaut/astrodyn/wiki/Frame-Tree-ECS-Native#13-migration-sequencing
;
/// Bidirectional handle linking a body / source / planet entity to its
/// frame entity in the ECS hierarchy. Inserted by
/// `register_*_frames_system` for every entity that carries dynamics
/// state. Internal physics consumers (gravity, integration,
/// frame-switch) and mission code via [`crate::frame_param`] read this
/// handle and walk `Query<&ChildOf>` from the frame entity to recover
/// the body's integration frame, the source's child frames, etc. The
/// frame entity itself carries [`FrameTransC`] / [`FrameRotC`] /
/// [`FrameAngVelC`] (the per-node state).
;
/// Frame entity for a source's planet-fixed (pfix) child frame.
///
/// Inserted by `register_pfix_frames_system` for every gravity source
/// that carries [`PlanetFixedRotationC`] and a non-`None`
/// [`RotationModelC`]. Removed when the rotation model toggles to
/// `None` (in which case the underlying ECS entity is retained as
/// [`RetiredPfixFrameEntityC`] for reuse on the next toggle back to a
/// rotating model — see that component's docs).
;
/// Hidden component that stashes a previously-spawned pfix *frame
/// entity* (the canonical [`PfixFrameEntityC`]) on a source whose
/// [`RotationModelC`] just toggled to
/// [`RotationModel::None`](astrodyn::RotationModel::None). The
/// public [`PfixFrameEntityC`] is removed at the same time so any
/// reader branching on its presence correctly observes "no
/// planet-fixed frame", but the orphan ECS entity itself is kept
/// alive — its `Name` is renamed to a `.retired` sentinel and its
/// `FrameRotC`/`FrameAngVelC` are reset to identity — so the next
/// toggle back to a rotating model can reuse it instead of spawning
/// a fresh entity.
///
/// Without this, every `None → rotating → None → rotating …` toggle
/// cycle would leak a fresh `<name>.frame.pfix` entity per cycle,
/// since [`crate::systems::register_pfix_frames_system`] filters by
/// `Without<PfixFrameEntityC>` and unconditionally spawns a new
/// entity for any source missing the public component.
;