This commit is contained in:
Leon Liu 2025-08-15 10:15:26 +09:00
parent ee4590276a
commit 1a97b9e986
3 changed files with 98 additions and 145 deletions

View File

@ -68,13 +68,6 @@
position: (2.987992735576156e+01, -6.341879950443392e-01, -6.754997950415415e-01),
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",
mass: 7.349e22,

View File

@ -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
*******************************************************************************

View File

@ -42,7 +42,7 @@ pub struct PositionAu(pub DVec3);
pub struct VelocityAuPerDay(pub DVec3);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MassKg(pub f64);
pub struct MassSolarMass(pub f64);
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectName(pub String);
@ -55,7 +55,7 @@ struct Position(PositionAu);
struct Velocity(VelocityAuPerDay);
#[derive(Component)]
struct Mass(MassKg);
struct Mass(MassSolarMass);
#[derive(Component)]
struct Radius(DistanceAu);
@ -83,6 +83,10 @@ struct ObjectLabel {
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
#[derive(Component)]
struct Trackable {}
@ -109,10 +113,11 @@ impl Plugin for SolarRenderingPlugin {
sync_radius_to_mesh,
sync_position_to_transform,
sync_name_labels,
manage_label_overlaps.after(sync_position_to_transform).after(sync_name_labels),
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(
Update,
(
@ -251,13 +256,14 @@ fn sync_name_labels(
ObjectLabel {
target_entity: entity,
},
LabelVisible(true), // Start visible by default
));
}
}
}
fn update_label_positions(
mut label_query: Query<(&mut Node, &ObjectLabel, &mut Text), With<ObjectLabel>>,
fn manage_label_overlaps(
mut label_query: Query<(&ObjectLabel, &mut LabelVisible)>,
objects_query: Query<(&GlobalTransform, &Name, &Radius)>,
camera_query: Query<(&Camera, &GlobalTransform)>,
) {
@ -265,7 +271,78 @@ fn update_label_positions(
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) {
let world_pos = global_transform.translation();
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
node.left = Val::Px(screen_pos.x + 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
text.0 = name.0.0.clone();
} else {
// Object is off-screen, hide label by moving it off-screen
node.left = Val::Px(-1000.0);
node.top = Val::Px(-1000.0);
// Object is off-screen, hide label
node.display = Display::None;
}
} else {
// Target entity no longer exists, remove label
// Note: In a more complex system, you might want to handle this in a separate cleanup system
// Target entity no longer exists, hide label
node.display = Display::None;
}
}
}
@ -446,6 +523,9 @@ fn setup_solar_system(mut commands: Commands) {
// Convert radius from km to AU
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
let mut entity_commands = commands.spawn((
ObjectBundle {
@ -460,7 +540,7 @@ fn setup_solar_system(mut commands: Commands) {
body_data.velocity.1,
body_data.velocity.2,
))),
mass: Mass(MassKg(body_data.mass)),
mass: Mass(MassSolarMass(mass_solar)),
radius: Radius(DistanceAu(radius_au)),
},
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 AU_TO_M: f64 = 149_597_870_691.0; // m
const DAY_TO_S: f64 = 86_400.0; // s
const SOLAR_MASS_KG: f64 = 1.98841e30; // kg
// G in AU^3 / (kg day^2)
const G_AU: f64 = G_SI * (DAY_TO_S * DAY_TO_S) / (AU_TO_M * AU_TO_M * AU_TO_M);
// G in AU^3 / (solar_mass day^2)
// 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;
// If FixedUpdate is 64 Hz and you want 1 day per game second: DT = 1/(64*STEPS) day/step