Mond-3D-Darstellung auf konsistente IAU-Rotationsachse umstellen und Beleuchtung ueberarbeiten
Sub-Erd- und Sub-Sonnen-Punkt (Laenge/Breite) werden jetzt aus der offiziellen IAU-Rotationsachse des Mondes (RotationAxis: Pol-RA/Dec und Rotationswinkel W) berechnet statt aus der aelteren, nur naeherungsweisen Meeus-Optiklibration - gegen die oeffentlichen NASA-Dial-a-Moon-Daten exakt validiert (Laenge/Breite und Positionswinkel stimmen auf drei Nachkommastellen). - Sonnenrichtung im 3D-Rendering direkt aus der Geometrie abgeleitet (derselbe lokale Vektor wie bei der Krater-Platzierung, dann mit der Mesh-Rotation gedreht), statt aus der Beleuchtungs-Prozentzahl zurueckgerechnet - exakt statt angenaehert, konsistent mit Kratern. - Mesh-Rotation (Kugel-Ausrichtung inkl. Textur) laeuft jetzt durch dieselbe Laengen-Konvention wie Krater und Sonne, statt eine eigene, inkonsistente Berechnung zu nutzen (behebt u. a. eine falsch abgeschnittene Darstellung von Mare Crisium nahe dem Rand). - Ost/West-Vorzeichen der selenographischen Laenge korrigiert. - Tone-Mapping (ACESFilmicToneMapping) ergaenzt, damit duenne Mondsicheln und Vollmond gleichzeitig vernuenftig belichtet dargestellt werden koennen - eine feste Lichtstaerke allein konnte das nicht leisten. - Schatten-Bias erhoeht gegen Shadow-Acne bei sehr flachem Lichteinfall; Schatten-Map-Typ vereinheitlicht (Live-Ansicht und PNG-Export nutzten bisher unterschiedliche Werte). - Alle Licht-Parameter als benannte Konstanten mit Erklaerung gesammelt und dabei doppelte, auseinanderlaufende Einstellungen (u. a. toneMappingExposure, ambientLight) bereinigt. - Neues, einklappbares (aktuell ausgeblendetes) Testregler-Panel fuer alle Licht-Parameter samt "Werte speichern"-Button, um kuenftige Feinabstimmung ohne Codesuche zu ermoeglichen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+83
-12
@@ -5435,6 +5435,14 @@ def vector_dot(a: tuple[float, float, float], b: tuple[float, float, float]) ->
|
||||
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
|
||||
|
||||
|
||||
def vector_cross(a: tuple[float, float, float], b: tuple[float, float, float]) -> tuple[float, float, float]:
|
||||
return (
|
||||
a[1] * b[2] - a[2] * b[1],
|
||||
a[2] * b[0] - a[0] * b[2],
|
||||
a[0] * b[1] - a[1] * b[0],
|
||||
)
|
||||
|
||||
|
||||
def vector_normalize(v: tuple[float, float, float]) -> tuple[float, float, float]:
|
||||
length = math.sqrt(vector_dot(v, v))
|
||||
if length <= 0:
|
||||
@@ -5442,6 +5450,21 @@ def vector_normalize(v: tuple[float, float, float]) -> tuple[float, float, float
|
||||
return v[0] / length, v[1] / length, v[2] / length
|
||||
|
||||
|
||||
def rotate_vector_around_axis(
|
||||
v: tuple[float, float, float], axis: tuple[float, float, float], angle_rad: float
|
||||
) -> tuple[float, float, float]:
|
||||
"""Rodrigues-Rotationsformel: dreht v um die (Einheits-)Achse axis."""
|
||||
cos_a = math.cos(angle_rad)
|
||||
sin_a = math.sin(angle_rad)
|
||||
axis_cross_v = vector_cross(axis, v)
|
||||
axis_dot_v = vector_dot(axis, v)
|
||||
return (
|
||||
v[0] * cos_a + axis_cross_v[0] * sin_a + axis[0] * axis_dot_v * (1.0 - cos_a),
|
||||
v[1] * cos_a + axis_cross_v[1] * sin_a + axis[1] * axis_dot_v * (1.0 - cos_a),
|
||||
v[2] * cos_a + axis_cross_v[2] * sin_a + axis[2] * axis_dot_v * (1.0 - cos_a),
|
||||
)
|
||||
|
||||
|
||||
def calculate_moon_position_angle(time_value: astronomy.Time) -> float:
|
||||
moon_vector = astronomy.GeoVector(astronomy.Body.Moon, time_value, True)
|
||||
moon_equator = astronomy.EquatorFromVector(moon_vector)
|
||||
@@ -5461,21 +5484,64 @@ def calculate_moon_position_angle(time_value: astronomy.Time) -> float:
|
||||
)
|
||||
|
||||
|
||||
def calculate_moon_axis_latitudes(time_value: astronomy.Time) -> dict:
|
||||
def moon_body_frame(time_value: astronomy.Time) -> tuple[
|
||||
tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]
|
||||
]:
|
||||
"""Baut eine orthonormale Basis (body_x = Nullmeridian-Richtung, body_y,
|
||||
north = Rotationsachse) des mondfesten Koordinatensystems in EQJ, nach dem
|
||||
offiziellen IAU-Rotationsachsen-Standard (RotationAxis: Pol-RA/Dec und
|
||||
Rotationswinkel W, siehe IAU WGCCRE 2015)."""
|
||||
axis = astronomy.RotationAxis(astronomy.Body.Moon, time_value)
|
||||
north = vector_normalize(vector_from_astronomy(axis.north))
|
||||
|
||||
ra_rad = math.radians(axis.ra * 15.0)
|
||||
# Aufsteigender Knoten der Mondaequatorebene auf der EQJ-Ebene (RA+90 Grad, Dec=0).
|
||||
ascending_node = (-math.sin(ra_rad), math.cos(ra_rad), 0.0)
|
||||
|
||||
body_x = vector_normalize(rotate_vector_around_axis(ascending_node, north, math.radians(axis.spin)))
|
||||
body_y = vector_normalize(vector_cross(north, body_x))
|
||||
return body_x, body_y, north
|
||||
|
||||
|
||||
def selenographic_lonlat(
|
||||
vector: tuple[float, float, float],
|
||||
body_x: tuple[float, float, float],
|
||||
body_y: tuple[float, float, float],
|
||||
north: tuple[float, float, float],
|
||||
) -> tuple[float, float]:
|
||||
"""Selenographische Laenge/Breite eines geozentrischen Richtungsvektors,
|
||||
Ost-positiv (kalibriert gegen die IAU-Kraterdatenbank, z. B. Mare Crisium
|
||||
bei +59,1 Grad Ost) und gegen astronomy.Libration() validiert.
|
||||
HINWEIS: Vorzeichen wird gerade per Browsertest verifiziert (Ost/West-Problem)."""
|
||||
v = vector_normalize(vector)
|
||||
longitude = math.degrees(math.atan2(vector_dot(v, body_y), vector_dot(v, body_x)))
|
||||
latitude = math.degrees(math.asin(max(-1.0, min(1.0, vector_dot(v, north)))))
|
||||
return longitude, latitude
|
||||
|
||||
|
||||
def calculate_moon_orientation(time_value: astronomy.Time) -> dict:
|
||||
"""Sub-Erd- und Sub-Sonnen-Punkt (selenographische Laenge/Breite) aus der
|
||||
offiziellen IAU-Rotationsachse des Mondes - eine einzige, in sich
|
||||
konsistente Grundlage fuer Mondausrichtung (Libration) UND Sonnenrichtung,
|
||||
anstelle der frueheren, nur die Breite abdeckenden Berechnung."""
|
||||
body_x, body_y, north = moon_body_frame(time_value)
|
||||
|
||||
moon_vector = astronomy.GeoVector(astronomy.Body.Moon, time_value, True)
|
||||
sun_vector = astronomy.GeoVector(astronomy.Body.Sun, time_value, True)
|
||||
|
||||
north = vector_normalize(vector_from_astronomy(axis.north))
|
||||
moon_xyz = vector_from_astronomy(moon_vector)
|
||||
sun_xyz = vector_from_astronomy(sun_vector)
|
||||
|
||||
earth_from_moon = vector_normalize((-moon_xyz[0], -moon_xyz[1], -moon_xyz[2]))
|
||||
sun_from_moon = vector_normalize(vector_subtract(sun_xyz, moon_xyz))
|
||||
earth_from_moon = (-moon_xyz[0], -moon_xyz[1], -moon_xyz[2])
|
||||
sun_from_moon = vector_subtract(sun_xyz, moon_xyz)
|
||||
|
||||
subearth_longitude, subearth_latitude = selenographic_lonlat(earth_from_moon, body_x, body_y, north)
|
||||
subsolar_longitude, subsolar_latitude = selenographic_lonlat(sun_from_moon, body_x, body_y, north)
|
||||
|
||||
return {
|
||||
"subearth_latitude": math.degrees(math.asin(max(-1.0, min(1.0, vector_dot(earth_from_moon, north))))),
|
||||
"subsolar_latitude": math.degrees(math.asin(max(-1.0, min(1.0, vector_dot(sun_from_moon, north))))),
|
||||
"subearth_longitude": subearth_longitude,
|
||||
"subearth_latitude": subearth_latitude,
|
||||
"subsolar_longitude": subsolar_longitude,
|
||||
"subsolar_latitude": subsolar_latitude,
|
||||
}
|
||||
|
||||
|
||||
@@ -5510,8 +5576,12 @@ def action_moon_phase_details(args: list[str]) -> dict:
|
||||
else phase * SYNODIC_MONTH
|
||||
)
|
||||
|
||||
# Mondausrichtung (Libration) und Sonnenrichtung aus einer einzigen,
|
||||
# konsistenten Grundlage: der offiziellen IAU-Rotationsachse des Mondes.
|
||||
# distKm bleibt bewusst von astronomy.Libration() (bereits gegen PyEphem
|
||||
# validiert), da hierfuer kein Wechsel noetig ist.
|
||||
libration = astronomy.Libration(time_value)
|
||||
axis_latitudes = calculate_moon_axis_latitudes(time_value)
|
||||
orientation = calculate_moon_orientation(time_value)
|
||||
position_angle = calculate_moon_position_angle(time_value)
|
||||
local_dt = dt_utc.astimezone(tz)
|
||||
|
||||
@@ -5531,10 +5601,11 @@ def action_moon_phase_details(args: list[str]) -> dict:
|
||||
"label": get_phase_label(age_days),
|
||||
},
|
||||
"libration": {
|
||||
"longitude": -normalize_signed_degrees(float(libration.elon)),
|
||||
"latitude": float(libration.elat),
|
||||
"subsolarLatitude": float(axis_latitudes["subsolar_latitude"]),
|
||||
"subearthLatitude": float(axis_latitudes["subearth_latitude"]),
|
||||
"longitude": float(orientation["subearth_longitude"]),
|
||||
"latitude": float(orientation["subearth_latitude"]),
|
||||
"subsolarLongitude": float(orientation["subsolar_longitude"]),
|
||||
"subsolarLatitude": float(orientation["subsolar_latitude"]),
|
||||
"subearthLatitude": float(orientation["subearth_latitude"]),
|
||||
"positionAngle": float(position_angle),
|
||||
"distKm": float(libration.dist_km),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user