update
This commit is contained in:
parent
ee4590276a
commit
1a97b9e986
@ -68,13 +68,6 @@
|
|||||||
position: (2.987992735576156e+01, -6.341879950443392e-01, -6.754997950415415e-01),
|
position: (2.987992735576156e+01, -6.341879950443392e-01, -6.754997950415415e-01),
|
||||||
velocity: (3.941595250081164e-05, 3.160389775728832e-03, -6.636996572427530e-05),
|
velocity: (3.941595250081164e-05, 3.160389775728832e-03, -6.636996572427530e-05),
|
||||||
),
|
),
|
||||||
(
|
|
||||||
name: "Pluto",
|
|
||||||
mass: 1.307e22,
|
|
||||||
radius: 1188.3,
|
|
||||||
position: (1.822881632666475e+01, -3.000801293901950e+01, -2.059815785829023e+00),
|
|
||||||
velocity: (2.758977855246929e-03, 9.457264850164455e-04, -8.880815395819214e-04),
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
name: "Moon",
|
name: "Moon",
|
||||||
mass: 7.349e22,
|
mass: 7.349e22,
|
||||||
|
|||||||
@ -1,124 +0,0 @@
|
|||||||
API VERSION: 1.2
|
|
||||||
API SOURCE: NASA/JPL Horizons API
|
|
||||||
|
|
||||||
*******************************************************************************
|
|
||||||
Revised: Apr 03, 2024 134340 Pluto 999
|
|
||||||
|
|
||||||
Pre-computed solution PLU060/DE440. Fit to post New Horizons encounter and
|
|
||||||
Gaia data through 2023. For discussion, see ...
|
|
||||||
|
|
||||||
M. Brozovic, R. A. Jacobson (2024) "Post-New Horizons orbits and masses
|
|
||||||
for the satellites of Pluto". AJ (in press)
|
|
||||||
|
|
||||||
PHYSICAL DATA (updated 2021-Jun-07; Mc= Charon mass, radius is IAU 2015):
|
|
||||||
Mass x10^22 (kg) = 1.307+-0.018 Volume, 10^10 km^3 = 0.697
|
|
||||||
GM (planet) km^3/s^2 = 869.326 Density (R=1195 km) = 1.86 g/cm^3
|
|
||||||
GM 1-sigma, km^3/s^2 = 0.4 Surface gravity = 0.611 m/s^2
|
|
||||||
Vol. mean radius (km) = 1188.3+-1.6 Mass ratio (Mc/Mp) = 0.122
|
|
||||||
Sidereal rot. period = 153.29335198 h Sid. rot. rat, rad/s = 0.0000113856
|
|
||||||
Mean solar day, h = 153.2820 Mean orbit velocity = 4.67 km/s
|
|
||||||
Sidereal orbit period = 249.58932 yr Escape speed, km/s = 1.21
|
|
||||||
Perihelion Aphelion Mean
|
|
||||||
Solar Constant (W/m^2) 1.56 0.56 0.88
|
|
||||||
Maximum Planetary IR (W/m^2) 0.8 0.3 0.5
|
|
||||||
Minimum Planetary IR (W/m^2) 0.8 0.3 0.5
|
|
||||||
*******************************************************************************
|
|
||||||
|
|
||||||
|
|
||||||
*******************************************************************************
|
|
||||||
Ephemeris / API_USER Thu Aug 14 15:51:59 2025 Pasadena, USA / Horizons
|
|
||||||
*******************************************************************************
|
|
||||||
Target body name: Pluto (999) {source: plu060_merged}
|
|
||||||
Center body name: Sun (10) {source: plu060_merged}
|
|
||||||
Center-site name: BODY CENTER
|
|
||||||
*******************************************************************************
|
|
||||||
Start time : A.D. 2025-Jan-01 00:00:00.0000 TDB
|
|
||||||
Stop time : A.D. 2025-Jan-01 00:01:00.0000 TDB
|
|
||||||
Step-size : 1 minutes
|
|
||||||
*******************************************************************************
|
|
||||||
Center geodetic : 0.0, 0.0, 0.0 {E-lon(deg),Lat(deg),Alt(km)}
|
|
||||||
Center cylindric: 0.0, 0.0, 0.0 {E-lon(deg),Dxy(km),Dz(km)}
|
|
||||||
Center radii : 695700.0, 695700.0, 695700.0 km {Equator_a, b, pole_c}
|
|
||||||
Output units : AU-D
|
|
||||||
Calendar mode : Mixed Julian/Gregorian
|
|
||||||
Output type : GEOMETRIC cartesian states
|
|
||||||
Output format : 2 (position and velocity)
|
|
||||||
Reference frame : Ecliptic of J2000.0
|
|
||||||
*******************************************************************************
|
|
||||||
JDTDB
|
|
||||||
X Y Z
|
|
||||||
VX VY VZ
|
|
||||||
*******************************************************************************
|
|
||||||
$$SOE
|
|
||||||
2460676.500000000 = A.D. 2025-Jan-01 00:00:00.0000 TDB
|
|
||||||
X = 1.822881632666475E+01 Y =-3.000801293901950E+01 Z =-2.059815785829023E+00
|
|
||||||
VX= 2.758977855246929E-03 VY= 9.457264850164455E-04 VZ=-8.880815395819214E-04
|
|
||||||
2460676.500694444 = A.D. 2025-Jan-01 00:01:00.0000 TDB
|
|
||||||
X = 1.822881824262399E+01 Y =-3.000801228226284E+01 Z =-2.059816402553139E+00
|
|
||||||
VX= 2.758984750779286E-03 VY= 9.457327203036255E-04 VZ=-8.880839212592113E-04
|
|
||||||
$$EOE
|
|
||||||
*******************************************************************************
|
|
||||||
|
|
||||||
TIME
|
|
||||||
|
|
||||||
Barycentric Dynamical Time ("TDB" or T_eph) output was requested. This
|
|
||||||
continuous coordinate time is equivalent to the relativistic proper time
|
|
||||||
of a clock at rest in a reference frame co-moving with the solar system
|
|
||||||
barycenter but outside the system's gravity well. It is the independent
|
|
||||||
variable in the solar system relativistic equations of motion.
|
|
||||||
|
|
||||||
TDB runs at a uniform rate of one SI second per second and is independent
|
|
||||||
of irregularities in Earth's rotation.
|
|
||||||
|
|
||||||
CALENDAR SYSTEM
|
|
||||||
|
|
||||||
Mixed calendar mode was active such that calendar dates after AD 1582-Oct-15
|
|
||||||
(if any) are in the modern Gregorian system. Dates prior to 1582-Oct-5 (if any)
|
|
||||||
are in the Julian calendar system, which is automatically extended for dates
|
|
||||||
prior to its adoption on 45-Jan-1 BC. The Julian calendar is useful for
|
|
||||||
matching historical dates. The Gregorian calendar more accurately corresponds
|
|
||||||
to the Earth's orbital motion and seasons. A "Gregorian-only" calendar mode is
|
|
||||||
available if such physical events are the primary interest.
|
|
||||||
|
|
||||||
REFERENCE FRAME AND COORDINATES
|
|
||||||
|
|
||||||
Ecliptic at the standard reference epoch
|
|
||||||
|
|
||||||
Reference epoch: J2000.0
|
|
||||||
X-Y plane: adopted Earth orbital plane at the reference epoch
|
|
||||||
Note: IAU76 obliquity of 84381.448 arcseconds wrt ICRF X-Y plane
|
|
||||||
X-axis : ICRF
|
|
||||||
Z-axis : perpendicular to the X-Y plane in the directional (+ or -) sense
|
|
||||||
of Earth's north pole at the reference epoch.
|
|
||||||
|
|
||||||
Symbol meaning [1 au= 149597870.700 km, 1 day= 86400.0 s]:
|
|
||||||
|
|
||||||
JDTDB Julian Day Number, Barycentric Dynamical Time
|
|
||||||
X X-component of position vector (au)
|
|
||||||
Y Y-component of position vector (au)
|
|
||||||
Z Z-component of position vector (au)
|
|
||||||
VX X-component of velocity vector (au/day)
|
|
||||||
VY Y-component of velocity vector (au/day)
|
|
||||||
VZ Z-component of velocity vector (au/day)
|
|
||||||
|
|
||||||
ABERRATIONS AND CORRECTIONS
|
|
||||||
|
|
||||||
Geometric state vectors have NO corrections or aberrations applied.
|
|
||||||
|
|
||||||
Computations by ...
|
|
||||||
|
|
||||||
Solar System Dynamics Group, Horizons On-Line Ephemeris System
|
|
||||||
4800 Oak Grove Drive, Jet Propulsion Laboratory
|
|
||||||
Pasadena, CA 91109 USA
|
|
||||||
|
|
||||||
General site: https://ssd.jpl.nasa.gov/
|
|
||||||
Mailing list: https://ssd.jpl.nasa.gov/email_list.html
|
|
||||||
System news : https://ssd.jpl.nasa.gov/horizons/news.html
|
|
||||||
User Guide : https://ssd.jpl.nasa.gov/horizons/manual.html
|
|
||||||
Connect : browser https://ssd.jpl.nasa.gov/horizons/app.html#/x
|
|
||||||
API https://ssd-api.jpl.nasa.gov/doc/horizons.html
|
|
||||||
command-line telnet ssd.jpl.nasa.gov 6775
|
|
||||||
e-mail/batch https://ssd.jpl.nasa.gov/ftp/ssd/horizons_batch.txt
|
|
||||||
scripts https://ssd.jpl.nasa.gov/ftp/ssd/SCRIPTS
|
|
||||||
Author : Jon.D.Giorgini@jpl.nasa.gov
|
|
||||||
*******************************************************************************
|
|
||||||
112
src/main.rs
112
src/main.rs
@ -42,7 +42,7 @@ pub struct PositionAu(pub DVec3);
|
|||||||
pub struct VelocityAuPerDay(pub DVec3);
|
pub struct VelocityAuPerDay(pub DVec3);
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct MassKg(pub f64);
|
pub struct MassSolarMass(pub f64);
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct ObjectName(pub String);
|
pub struct ObjectName(pub String);
|
||||||
@ -55,7 +55,7 @@ struct Position(PositionAu);
|
|||||||
struct Velocity(VelocityAuPerDay);
|
struct Velocity(VelocityAuPerDay);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Mass(MassKg);
|
struct Mass(MassSolarMass);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Radius(DistanceAu);
|
struct Radius(DistanceAu);
|
||||||
@ -83,6 +83,10 @@ struct ObjectLabel {
|
|||||||
target_entity: Entity,
|
target_entity: Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Component to control label visibility
|
||||||
|
#[derive(Component)]
|
||||||
|
struct LabelVisible(bool);
|
||||||
|
|
||||||
// Component to mark objects that can be focused on with proper zoom levels
|
// Component to mark objects that can be focused on with proper zoom levels
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Trackable {}
|
struct Trackable {}
|
||||||
@ -109,10 +113,11 @@ impl Plugin for SolarRenderingPlugin {
|
|||||||
sync_radius_to_mesh,
|
sync_radius_to_mesh,
|
||||||
sync_position_to_transform,
|
sync_position_to_transform,
|
||||||
sync_name_labels,
|
sync_name_labels,
|
||||||
|
manage_label_overlaps.after(sync_position_to_transform).after(sync_name_labels),
|
||||||
camera_follow_system.after(sync_position_to_transform),
|
camera_follow_system.after(sync_position_to_transform),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.add_systems(PostUpdate, update_label_positions)
|
.add_systems(PostUpdate, update_label_positions.after(manage_label_overlaps))
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
@ -251,13 +256,14 @@ fn sync_name_labels(
|
|||||||
ObjectLabel {
|
ObjectLabel {
|
||||||
target_entity: entity,
|
target_entity: entity,
|
||||||
},
|
},
|
||||||
|
LabelVisible(true), // Start visible by default
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_label_positions(
|
fn manage_label_overlaps(
|
||||||
mut label_query: Query<(&mut Node, &ObjectLabel, &mut Text), With<ObjectLabel>>,
|
mut label_query: Query<(&ObjectLabel, &mut LabelVisible)>,
|
||||||
objects_query: Query<(&GlobalTransform, &Name, &Radius)>,
|
objects_query: Query<(&GlobalTransform, &Name, &Radius)>,
|
||||||
camera_query: Query<(&Camera, &GlobalTransform)>,
|
camera_query: Query<(&Camera, &GlobalTransform)>,
|
||||||
) {
|
) {
|
||||||
@ -265,7 +271,78 @@ fn update_label_positions(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (mut node, label, mut text) in label_query.iter_mut() {
|
// Collect all on-screen objects with their screen positions and radii
|
||||||
|
let mut visible_objects: Vec<(Entity, Vec2, f64, String)> = Vec::new();
|
||||||
|
|
||||||
|
for (label, _) in label_query.iter() {
|
||||||
|
if let Ok((global_transform, name, radius)) = objects_query.get(label.target_entity) {
|
||||||
|
let world_pos = global_transform.translation();
|
||||||
|
if let Ok(screen_pos) = camera.world_to_viewport(camera_transform, world_pos) {
|
||||||
|
visible_objects.push((
|
||||||
|
label.target_entity,
|
||||||
|
screen_pos,
|
||||||
|
radius.0.0, // radius in AU
|
||||||
|
name.0.0.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset all labels to visible first
|
||||||
|
for (_, mut visible) in label_query.iter_mut() {
|
||||||
|
visible.0 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for overlaps and hide smaller objects
|
||||||
|
const OVERLAP_THRESHOLD: f32 = 50.0; // pixels
|
||||||
|
|
||||||
|
for i in 0..visible_objects.len() {
|
||||||
|
for j in (i + 1)..visible_objects.len() {
|
||||||
|
let (entity_a, pos_a, radius_a, name_a) = &visible_objects[i];
|
||||||
|
let (entity_b, pos_b, radius_b, name_b) = &visible_objects[j];
|
||||||
|
|
||||||
|
let distance = pos_a.distance(*pos_b);
|
||||||
|
if distance < OVERLAP_THRESHOLD {
|
||||||
|
// Determine which label to hide based on radius (larger wins)
|
||||||
|
let entity_to_hide = if radius_a > radius_b {
|
||||||
|
*entity_b
|
||||||
|
} else if radius_b > radius_a {
|
||||||
|
*entity_a
|
||||||
|
} else {
|
||||||
|
// If radii are equal, prefer alphabetically first name
|
||||||
|
if name_a < name_b { *entity_b } else { *entity_a }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hide the smaller object's label
|
||||||
|
for (label, mut visible) in label_query.iter_mut() {
|
||||||
|
if label.target_entity == entity_to_hide {
|
||||||
|
visible.0 = false;
|
||||||
|
// Debug: which label got hidden
|
||||||
|
debug!("Hiding label due to overlap (distance: {:.1}px)", distance);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_label_positions(
|
||||||
|
mut label_query: Query<(&mut Node, &ObjectLabel, &mut Text, &LabelVisible), With<ObjectLabel>>,
|
||||||
|
objects_query: Query<(&GlobalTransform, &Name, &Radius)>,
|
||||||
|
camera_query: Query<(&Camera, &GlobalTransform)>,
|
||||||
|
) {
|
||||||
|
let Ok((camera, camera_transform)) = camera_query.single() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (mut node, label, mut text, visible) in label_query.iter_mut() {
|
||||||
|
if !visible.0 {
|
||||||
|
// Label is marked as invisible due to overlap, hide it
|
||||||
|
node.display = Display::None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok((global_transform, name, _radius)) = objects_query.get(label.target_entity) {
|
if let Ok((global_transform, name, _radius)) = objects_query.get(label.target_entity) {
|
||||||
let world_pos = global_transform.translation();
|
let world_pos = global_transform.translation();
|
||||||
if let Ok(screen_pos) = camera.world_to_viewport(camera_transform, world_pos) {
|
if let Ok(screen_pos) = camera.world_to_viewport(camera_transform, world_pos) {
|
||||||
@ -275,17 +352,17 @@ fn update_label_positions(
|
|||||||
// Position the label on screen, offset slightly to avoid overlapping the object
|
// Position the label on screen, offset slightly to avoid overlapping the object
|
||||||
node.left = Val::Px(screen_pos.x + scaled_offset);
|
node.left = Val::Px(screen_pos.x + scaled_offset);
|
||||||
node.top = Val::Px(screen_pos.y - scaled_offset);
|
node.top = Val::Px(screen_pos.y - scaled_offset);
|
||||||
|
node.display = Display::Flex; // Make sure it's visible
|
||||||
|
|
||||||
// Update text in case name changed
|
// Update text in case name changed
|
||||||
text.0 = name.0.0.clone();
|
text.0 = name.0.0.clone();
|
||||||
} else {
|
} else {
|
||||||
// Object is off-screen, hide label by moving it off-screen
|
// Object is off-screen, hide label
|
||||||
node.left = Val::Px(-1000.0);
|
node.display = Display::None;
|
||||||
node.top = Val::Px(-1000.0);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Target entity no longer exists, remove label
|
// Target entity no longer exists, hide label
|
||||||
// Note: In a more complex system, you might want to handle this in a separate cleanup system
|
node.display = Display::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -445,6 +522,9 @@ fn setup_solar_system(mut commands: Commands) {
|
|||||||
for body_data in initial_state.bodies {
|
for body_data in initial_state.bodies {
|
||||||
// Convert radius from km to AU
|
// Convert radius from km to AU
|
||||||
let radius_au = body_data.radius / 149597870.691; // km to AU conversion
|
let radius_au = body_data.radius / 149597870.691; // km to AU conversion
|
||||||
|
|
||||||
|
// Convert mass from kg to solar masses
|
||||||
|
let mass_solar = body_data.mass / SOLAR_MASS_KG;
|
||||||
|
|
||||||
// Create base bundle
|
// Create base bundle
|
||||||
let mut entity_commands = commands.spawn((
|
let mut entity_commands = commands.spawn((
|
||||||
@ -460,7 +540,7 @@ fn setup_solar_system(mut commands: Commands) {
|
|||||||
body_data.velocity.1,
|
body_data.velocity.1,
|
||||||
body_data.velocity.2,
|
body_data.velocity.2,
|
||||||
))),
|
))),
|
||||||
mass: Mass(MassKg(body_data.mass)),
|
mass: Mass(MassSolarMass(mass_solar)),
|
||||||
radius: Radius(DistanceAu(radius_au)),
|
radius: Radius(DistanceAu(radius_au)),
|
||||||
},
|
},
|
||||||
Trackable {},
|
Trackable {},
|
||||||
@ -485,9 +565,13 @@ fn setup_solar_system(mut commands: Commands) {
|
|||||||
const G_SI: f64 = 6.67430e-11; // m^3 / (kg s^2)
|
const G_SI: f64 = 6.67430e-11; // m^3 / (kg s^2)
|
||||||
const AU_TO_M: f64 = 149_597_870_691.0; // m
|
const AU_TO_M: f64 = 149_597_870_691.0; // m
|
||||||
const DAY_TO_S: f64 = 86_400.0; // s
|
const DAY_TO_S: f64 = 86_400.0; // s
|
||||||
|
const SOLAR_MASS_KG: f64 = 1.98841e30; // kg
|
||||||
|
|
||||||
// G in AU^3 / (kg day^2)
|
// G in AU^3 / (solar_mass day^2)
|
||||||
const G_AU: f64 = G_SI * (DAY_TO_S * DAY_TO_S) / (AU_TO_M * AU_TO_M * AU_TO_M);
|
// G_SI has units m^3 / (kg s^2)
|
||||||
|
// We want AU^3 / (solar_mass day^2)
|
||||||
|
// G_AU = G_SI * (day^2 / s^2) * (kg / solar_mass) * (m^3 / AU^3)
|
||||||
|
const G_AU: f64 = G_SI * (DAY_TO_S * DAY_TO_S) / (SOLAR_MASS_KG * AU_TO_M * AU_TO_M * AU_TO_M);
|
||||||
|
|
||||||
const STEPS: usize = 100;
|
const STEPS: usize = 100;
|
||||||
// If FixedUpdate is 64 Hz and you want 1 day per game second: DT = 1/(64*STEPS) day/step
|
// If FixedUpdate is 64 Hz and you want 1 day per game second: DT = 1/(64*STEPS) day/step
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user