Source code for ephemeris_tools.rendering.draw_view_impl

"""Implementation of draw_planetary_view (port of rspk_drawview.f main routine)."""

from __future__ import annotations

import math
from dataclasses import dataclass, field
from typing import TextIO

import cspyce

from ephemeris_tools.constants import DEFAULT_ALIGN_LOC_POINTS
from ephemeris_tools.rendering.draw_view_finish import draw_view_box_labels_stars_close
from ephemeris_tools.rendering.draw_view_helpers import (
    _DEVICE,
    _H1,
    _H2,
    _STAR_DIAMPTS,
    _V1,
    _V2,
    DARK_LINE,
    FOV_PTS,
    HALFPI,
    LIT_LINE,
    LOOP_DLON,
    LOOP_WIDTH,
    MAX_ARCPTS,
    MAX_MINSIZE,
    MAX_NLOOPS,
    MAX_NMOONS,
    MAX_NRINGS,
    MOON_LATS,
    MOON_MERIDS,
    NO_LINE,
    PLANET_LATS,
    PLANET_MERIDS,
    RING_THICKNESS,
    SHADOW_LINE,
    SUN_ID,
    TWOPI,
    _opsgnd,
    _rspk_draw_bodies,
    _rspk_draw_rings,
    _vhat,
    _vnorm,
    _vrotv,
    _write_ps_preamble,
    camera_matrix,
)
from ephemeris_tools.rendering.escher import (
    EscherState,
    EscherViewState,
    esfile,
    write_ps_header,
)
from ephemeris_tools.rendering.euclid import (
    EuclidState,
    eubody,
    eugeom,
    euview,
)
from ephemeris_tools.spice.bodmat import bodmat
from ephemeris_tools.spice.common import get_state
from ephemeris_tools.spice.observer import observer_state
from ephemeris_tools.spice.shifts import spkapp_shifted


[docs] @dataclass(frozen=True) class DrawPlanetaryViewOptions: """Options for draw_planetary_view (geometry, moons, rings, stars, captions).""" obs_time: float fov: float center_ra: float center_dec: float planet_name: str blank_disks: bool = False prime_pts: float = 0.0 nmoons: int = 0 moon_flags: list[bool] = field(default_factory=list) moon_ids: list[int] = field(default_factory=list) moon_names: list[str] = field(default_factory=list) moon_labelpts: float = 0.0 moon_diampts: float = 0.0 nrings: int = 0 ring_flags: list[bool] = field(default_factory=list) ring_rads: list[float] = field(default_factory=list) ring_elevs: list[float] = field(default_factory=list) ring_eccs: list[float] = field(default_factory=list) ring_incs: list[float] = field(default_factory=list) ring_peris: list[float] = field(default_factory=list) ring_nodes: list[float] = field(default_factory=list) ring_offsets: list[list[float]] = field(default_factory=list) ring_opaqs: list[bool] = field(default_factory=list) ring_dashed: list[bool] = field(default_factory=list) ring_method: int = 0 narcs: int = 0 arc_flags: list[bool] = field(default_factory=list) arc_rings: list[int] = field(default_factory=list) arc_minlons: list[float] = field(default_factory=list) arc_maxlons: list[float] = field(default_factory=list) arc_width: float = 4.0 nstars: int = 0 star_ras: list[float] = field(default_factory=list) star_decs: list[float] = field(default_factory=list) star_names: list[str] = field(default_factory=list) star_labels: bool = False star_diampts: float = _STAR_DIAMPTS title: str = '' ncaptions: int = 0 lcaptions: list[str] = field(default_factory=list) rcaptions: list[str] = field(default_factory=list) align_loc: float = DEFAULT_ALIGN_LOC_POINTS
[docs] def draw_planetary_view(output: TextIO, options: DrawPlanetaryViewOptions) -> None: """Generate PostScript showing planetary system at a time. Ported from RSPK_DrawView. Renders planet and moons as triaxial ellipsoids with terminators, optional rings (with vertical offsets and opacity), arcs, and stars. Frame is J2000 (dec up, RA left). Title and captions supported. Parameters: output: Open text stream for PostScript output. options: Geometry and display options (obs_time, fov, center, planet, moons, rings, arcs, stars, title, captions). See DrawPlanetaryViewOptions. """ spice_state = get_state() planet_id = spice_state.planet_id planet_num = spice_state.planet_num # Default list args; copy ring_flags before padding so we do not mutate caller's list moon_flags = list(options.moon_flags) moon_ids = list(options.moon_ids) moon_names = list(options.moon_names) ring_flags = list(options.ring_flags) ring_rads = list(options.ring_rads) ring_elevs = list(options.ring_elevs) ring_eccs = list(options.ring_eccs) ring_incs = list(options.ring_incs) ring_peris = list(options.ring_peris) ring_nodes = list(options.ring_nodes) ring_offsets = list(options.ring_offsets) ring_opaqs = list(options.ring_opaqs) ring_dashed = list(options.ring_dashed) arc_flags = list(options.arc_flags) arc_rings = list(options.arc_rings) arc_minlons = list(options.arc_minlons) arc_maxlons = list(options.arc_maxlons) star_ras = list(options.star_ras) star_decs = list(options.star_decs) star_names = list(options.star_names) lcaptions = list(options.lcaptions) if options.lcaptions else [] rcaptions = list(options.rcaptions) if options.rcaptions else [] out_name = getattr(output, 'name', '') or 'view.ps' # =================================================================== # Initialize the PostScript file # =================================================================== escher_state = EscherState() escher_state.outuni = output escher_state.open = True escher_state.external_stream = True escher_state.outfil = out_name escher_state.creator = f'{options.planet_name} Viewer, PDS Ring-Moon Systems Node' escher_state.fonts = 'Helvetica' esfile(out_name, escher_state.creator, escher_state.fonts, escher_state) escher_state.outuni = output escher_state.open = True escher_state.external_stream = True write_ps_header(escher_state) _write_ps_preamble( escher_state, options.planet_name, options.title, options.ncaptions, lcaptions, rcaptions, options.align_loc, options.moon_labelpts, ) # =================================================================== # Initialize camera # =================================================================== if not (0 < options.fov < math.pi): raise ValueError(f'options.fov must be in (0, pi), got {options.fov!r}') delta = math.tan(options.fov / 2.0) view_state = EscherViewState() euclid_state = EuclidState() euview( _DEVICE, _H1, _H2, _V1, _V2, -delta, delta, -delta, delta, euclid_state, view_state, escher_state, ) # Meridian and latitude count if options.blank_disks: pmerids, plats = 0, 0 mmerids, mlats = 0, 0 term_line = LIT_LINE else: pmerids, plats = PLANET_MERIDS, PLANET_LATS mmerids, mlats = MOON_MERIDS, MOON_LATS term_line = DARK_LINE # =================================================================== # Set up observer and planet geometry (SPICE calls) # =================================================================== # Observer state obs_pv = list(observer_state(options.obs_time)) # Planet state _planet_pv, planet_dt = cspyce.spkapp(planet_id, options.obs_time, 'J2000', obs_pv[:6], 'LT') planet_dpv = list(_planet_pv) planet_time = options.obs_time - planet_dt planet_pv = list(cspyce.spkssb(planet_id, planet_time, 'J2000')) # Planet rotation matrix (J2000 -> body frame) planet_mat = bodmat(planet_id, planet_time) # Sun location and radius sun_pv, _sun_dt = cspyce.spkapp(SUN_ID, planet_time, 'J2000', planet_pv[:6], 'LT+S') sun_dpv = list(sun_pv) sun_loc = [planet_pv[i] + sun_dpv[i] for i in range(3)] sun_radii_arr = cspyce.bodvar(SUN_ID, 'RADII') sun_rad = float(sun_radii_arr[0]) # Camera C-matrix cmat = camera_matrix(options.center_ra, options.center_dec) # =================================================================== # Build body list # =================================================================== nbodies = 0 body_locs: list[list[float]] = [] body_axes: list[list[list[float]]] = [] body_pts: list[float] = [] body_dist: list[float] = [] body_los: list[list[float]] = [] body_names_list: list[str] = [] # Body 1: Planet nbodies += 1 planet_loc = [obs_pv[i] + planet_dpv[i] for i in range(3)] body_locs.append(planet_loc) # planet_mat is J2000→body. Its rows ARE the body axes in J2000 # (matching FORTRAN: XPOSE then column extraction). p_radii_arr = cspyce.bodvar(planet_id, 'RADII') planet_axes_scaled = [[planet_mat[i][j] * p_radii_arr[i] for j in range(3)] for i in range(3)] body_axes.append(planet_axes_scaled) body_pts.append(0.0) body_dist.append(0.0) body_los.append([0.0, 0.0, 0.0]) body_names_list.append(' ') # Never label planet # Body 2: Dummy body at middle of field of view nbodies += 1 dummy_axes = [_vhat(planet_axes_scaled[i]) for i in range(3)] body_axes.append(dummy_axes) # Locate along optic axis, 2x planet distance tempvec = [planet_dpv[i] for i in range(3)] tdist = _vnorm(tempvec) optic_vec = [2.0 * tdist * cmat[j][2] for j in range(3)] dummy_loc = [obs_pv[i] + optic_vec[i] for i in range(3)] body_locs.append(dummy_loc) body_pts.append(0.0) body_dist.append(0.0) body_los.append([0.0, 0.0, 0.0]) body_names_list.append(' ') # Bodies 3+: Moons use_nmoons = min(options.nmoons, MAX_NMOONS) for imoon in range(use_nmoons): if imoon >= len(moon_flags) or not moon_flags[imoon]: continue if imoon >= len(moon_ids): continue mid = moon_ids[imoon] # Moon position try: moon_pv, mdt = spkapp_shifted(mid, options.obs_time, 'J2000', obs_pv[:6], 'LT') except Exception: continue moon_dpv = list(moon_pv) moon_loc = [obs_pv[i] + moon_dpv[i] for i in range(3)] body_locs.append(moon_loc) body_los.append(list(moon_dpv[:3])) # Moon axes - use moon's own BODMAT if available, else planet_mat try: if cspyce.bodfnd(mid, 'POLE_RA'): moon_rot = bodmat(mid, options.obs_time - mdt) moon_mat = [list(row) for row in moon_rot] else: moon_mat = [list(row) for row in planet_mat] except Exception: moon_mat = [list(row) for row in planet_mat] # moon_mat is J2000→body. Its rows are the body axes in J2000. try: m_radii_arr = list(cspyce.bodvar(mid, 'RADII')) except Exception: m_radii_arr = [1.0, 1.0, 1.0] moon_axes = [[moon_mat[i][j] * m_radii_arr[i] for j in range(3)] for i in range(3)] body_axes.append(moon_axes) # Projected diameter in points moon_dist_km = _vnorm(moon_dpv[:3]) body_pts_val = 2.0 * m_radii_arr[0] * FOV_PTS / (moon_dist_km * options.fov) body_pts.append(body_pts_val) # Name mname = moon_names[imoon] if imoon < len(moon_names) else '' body_names_list.append(mname) # Distance from planet tv = [moon_loc[i] - planet_loc[i] for i in range(3)] body_dist.append(_vnorm(tv)) nbodies += 1 # =================================================================== # Determine ring shapes (FORTRAN lines 698-820) # =================================================================== use_nrings = min(options.nrings, MAX_NRINGS) # Ensure ring and arc lists have at least use_nrings / narcs entries to avoid IndexError _def_f = False _def_0 = 0.0 _def_03 = [0.0, 0.0, 0.0] while len(ring_flags) < use_nrings: ring_flags.append(_def_f) while len(ring_rads) < use_nrings: ring_rads.append(_def_0) while len(ring_eccs) < use_nrings: ring_eccs.append(_def_0) while len(ring_nodes) < use_nrings: ring_nodes.append(_def_0) while len(ring_incs) < use_nrings: ring_incs.append(_def_0) while len(ring_peris) < use_nrings: ring_peris.append(_def_0) while len(ring_elevs) < use_nrings: ring_elevs.append(_def_0) while len(ring_offsets) < use_nrings: ring_offsets.append(_def_03) while len(ring_opaqs) < use_nrings: ring_opaqs.append(_def_f) while len(ring_dashed) < use_nrings: ring_dashed.append(_def_f) use_narcs = min( options.narcs, len(arc_rings), len(arc_flags), len(arc_minlons), len(arc_maxlons), ) while len(arc_rings) < options.narcs: arc_rings.append(0) while len(arc_flags) < options.narcs: arc_flags.append(_def_f) while len(arc_minlons) < options.narcs: arc_minlons.append(_def_0) while len(arc_maxlons) < options.narcs: arc_maxlons.append(_def_0) # Planet pole vector (reversed for Uranus) pole = _vhat(planet_axes_scaled[2]) if planet_num == 7: pole = [-pole[0], -pole[1], -pole[2]] # Equatorial plane ascending node (J2000) j2000_z = [0.0, 0.0, 1.0] ascnode = [ j2000_z[1] * pole[2] - j2000_z[2] * pole[1], j2000_z[2] * pole[0] - j2000_z[0] * pole[2], j2000_z[0] * pole[1] - j2000_z[1] * pole[0], ] # Extrapolate planet's relative location at observer-received time offset = [planet_dt * planet_pv[3 + i] for i in range(3)] r_ring_locs: list[list[float]] = [] r_ring_axes1: list[list[float]] = [] r_ring_axes2: list[list[float]] = [] r_ring_axes3: list[list[float]] = [] r_ring_dark: list[bool] = [] last_opaq = 0 nloops = 0 loop_locs: list[list[float]] = [] loop_axes1: list[list[float]] = [] loop_axes2: list[list[float]] = [] loop_ring: list[int] = [] for iring in range(use_nrings): # Initialize ring arrays to zeros even for skipped rings if not ring_flags[iring]: r_ring_locs.append([0.0, 0.0, 0.0]) r_ring_axes1.append([0.0, 0.0, 0.0]) r_ring_axes2.append([0.0, 0.0, 0.0]) r_ring_axes3.append([0.0, 0.0, 0.0]) r_ring_dark.append(False) continue rad = ring_rads[iring] if iring < len(ring_rads) else 0.0 ecc = ring_eccs[iring] if iring < len(ring_eccs) else 0.0 # Track outermost opaque ring if iring < len(ring_opaqs) and ring_opaqs[iring]: last_opaq = iring + 1 # 1-based # Compute ring pole and ascending node from ring inclination/node rn = ring_nodes[iring] if iring < len(ring_nodes) else 0.0 ri = ring_incs[iring] if iring < len(ring_incs) else 0.0 # VROTV(ascnode, pole, ring_nodes(iring)) -> ringnode ringnode = _vrotv(ascnode, pole, rn) # VROTV(pole, ringnode, ring_incs(iring)) -> ringpole ringpole = _vrotv(pole, ringnode, ri) ringpole = _vhat(ringpole) # Ring axes ring_ax3 = [RING_THICKNESS * ringpole[i] for i in range(3)] r_ring_axes3.append(ring_ax3) # Pericenter direction rp = ring_peris[iring] if iring < len(ring_peris) else 0.0 peri = _vrotv(ringnode, ringpole, rp - rn) peri = _vhat(peri) ring_ax1 = [rad * peri[i] for i in range(3)] r_ring_axes1.append(ring_ax1) # Minor axis = peri rotated 90 degrees around ringpole minor_dir = _vrotv(peri, ringpole, HALFPI) ring_ax2 = [rad * math.sqrt(1.0 - ecc * ecc) * minor_dir[i] for i in range(3)] r_ring_axes2.append(ring_ax2) # Ring center = planet_loc - ecc * major_axis + elevation + offset ring_loc = [-ecc * ring_ax1[i] + planet_loc[i] for i in range(3)] # Add vertical elevation re = ring_elevs[iring] if iring < len(ring_elevs) else 0.0 ring_loc = [ring_loc[i] + re * pole[i] for i in range(3)] # Add ring offset if iring < len(ring_offsets): ro = ring_offsets[iring] ring_loc = [ring_loc[i] + ro[i] for i in range(3)] r_ring_locs.append(ring_loc) # Determine if ring is dark (observer and Sun on opposite sides) tempvec_obs = [ring_loc[i] - obs_pv[i] + offset[i] for i in range(3)] dot1 = -( ringpole[0] * tempvec_obs[0] + ringpole[1] * tempvec_obs[1] + ringpole[2] * tempvec_obs[2] ) sun_hat = _vhat(sun_dpv[:3]) dot2 = ringpole[0] * sun_hat[0] + ringpole[1] * sun_hat[1] + ringpole[2] * sun_hat[2] is_dashed = ring_dashed[iring] if iring < len(ring_dashed) else False if is_dashed: r_ring_dark.append(False) else: sun_dist_val = _vnorm(sun_dpv[:3]) sun_angular = sun_rad / sun_dist_val if sun_dist_val > 0 else 0 r_ring_dark.append(_opsgnd(dot1, dot2) and abs(dot2) > sun_angular) # Arc loops for this ring for iarc in range(use_narcs): if iarc >= len(arc_rings) or arc_rings[iarc] != iring + 1: # 1-based comparison continue if iarc >= len(arc_flags) or not arc_flags[iarc]: continue # Mean anomaly range lon1 = (arc_minlons[iarc] if iarc < len(arc_minlons) else 0.0) - rp lon2 = (arc_maxlons[iarc] if iarc < len(arc_maxlons) else 0.0) - rp if lon2 < lon1: lon2 += TWOPI nsteps = max(int((lon2 - lon1) / LOOP_DLON), 1) dlon = (lon2 - lon1) / nsteps lon = lon1 - dlon for _istep in range(nsteps): lon += dlon if nloops >= MAX_NLOOPS: break nloops += 1 # Vectors from ring center to loop ends vec1 = [math.cos(lon) * ring_ax1[i] + math.sin(lon) * ring_ax2[i] for i in range(3)] vec2 = [ math.cos(lon + dlon) * ring_ax1[i] + math.sin(lon + dlon) * ring_ax2[i] for i in range(3) ] # Loop center and axes tmid = [0.5 * vec1[i] + 0.5 * vec2[i] for i in range(3)] la1 = [vec1[i] - tmid[i] for i in range(3)] la2 = _vhat(tmid) la2 = [LOOP_WIDTH * la2[i] for i in range(3)] ll = [tmid[i] + ring_loc[i] for i in range(3)] loop_axes1.append(la1) loop_axes2.append(la2) loop_locs.append(ll) loop_ring.append(iring + 1) # 1-based # =================================================================== # Render scene based on ring_method # =================================================================== use_diampts = min(MAX_MINSIZE, options.moon_diampts) use_arcpts = min(MAX_ARCPTS, options.arc_width) transparent_method = 0 semi_transparent_method = 1 # OPAQUE_METHOD = 2 if options.ring_method == transparent_method or last_opaq == 0: # Case 0: Transparent — simplest case eugeom( 1, [sun_loc], [sun_rad], obs_pv[:3], [cmat[0], cmat[1], cmat[2]], nbodies, body_locs, body_axes, euclid_state, ) _rspk_draw_bodies( nbodies=nbodies, body_pts=body_pts, body_names=body_names_list, body_dist=body_dist, body_diampts=use_diampts, update_names=True, mindist=0.0, pmerids=pmerids, plats=plats, mmerids=mmerids, mlats=mlats, lit_line=LIT_LINE, dark_line=DARK_LINE, term_line=term_line, lit_line2=LIT_LINE, dark_line2=DARK_LINE, term_line2=term_line, prime_pts=options.prime_pts, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) _rspk_draw_rings( iring1=1, iring2=use_nrings, ring_flags=ring_flags, ring_locs=r_ring_locs, ring_axes1=r_ring_axes1, ring_axes2=r_ring_axes2, ring_dark=r_ring_dark, ring_dashed=ring_dashed, nloops=nloops, loop_locs=loop_locs, loop_axes1=loop_axes1, loop_axes2=loop_axes2, loop_ring=loop_ring, arc_width=use_arcpts, lit_line=LIT_LINE, dark_line=DARK_LINE, shadow_line=SHADOW_LINE, term_line=term_line, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) elif options.ring_method == semi_transparent_method: # Case 1: Semi-transparent — two passes lo = last_opaq - 1 # 0-based index for outermost opaque ring # First pass: unlit bodies eugeom( 1, [sun_loc], [sun_rad], obs_pv[:3], [cmat[0], cmat[1], cmat[2]], nbodies, body_locs, body_axes, euclid_state, ) _rspk_draw_bodies( nbodies=nbodies, body_pts=body_pts, body_names=body_names_list, body_dist=body_dist, body_diampts=use_diampts, update_names=True, mindist=ring_rads[lo] if lo < len(ring_rads) else 0.0, pmerids=pmerids, plats=plats, mmerids=mmerids, mlats=mlats, lit_line=DARK_LINE, dark_line=DARK_LINE, term_line=DARK_LINE, lit_line2=LIT_LINE, dark_line2=DARK_LINE, term_line2=term_line, prime_pts=options.prime_pts, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) # Redefine with outermost opaque ring as flat ellipsoid ext_locs = [*body_locs, r_ring_locs[lo]] ext_axes = [*body_axes, [r_ring_axes1[lo], r_ring_axes2[lo], r_ring_axes3[lo]]] eugeom( 1, [sun_loc], [sun_rad], obs_pv[:3], [cmat[0], cmat[1], cmat[2]], nbodies + 1, ext_locs, ext_axes, euclid_state, ) # Re-draw lit, but not interior moons _rspk_draw_bodies( nbodies=nbodies, body_pts=body_pts, body_names=body_names_list, body_dist=body_dist, body_diampts=use_diampts, update_names=False, mindist=ring_rads[lo] if lo < len(ring_rads) else 0.0, pmerids=pmerids, plats=plats, mmerids=mmerids, mlats=mlats, lit_line=LIT_LINE, dark_line=DARK_LINE, term_line=term_line, lit_line2=NO_LINE, dark_line2=NO_LINE, term_line2=NO_LINE, prime_pts=options.prime_pts, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) # Draw opaque ring invisibly eubody( nbodies + 1, 0, 0, 1, NO_LINE, NO_LINE, NO_LINE, euclid_state, view_state, escher_state ) # Exterior rings _rspk_draw_rings( iring1=last_opaq + 1, iring2=use_nrings, ring_flags=ring_flags, ring_locs=r_ring_locs, ring_axes1=r_ring_axes1, ring_axes2=r_ring_axes2, ring_dark=r_ring_dark, ring_dashed=ring_dashed, nloops=nloops, loop_locs=loop_locs, loop_axes1=loop_axes1, loop_axes2=loop_axes2, loop_ring=loop_ring, arc_width=use_arcpts, lit_line=LIT_LINE, dark_line=DARK_LINE, shadow_line=SHADOW_LINE, term_line=term_line, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) # Re-define without rings eugeom( 1, [sun_loc], [sun_rad], obs_pv[:3], [cmat[0], cmat[1], cmat[2]], nbodies, body_locs, body_axes, euclid_state, ) # Set up bodies without drawing (for correct ring lighting) _rspk_draw_bodies( nbodies=nbodies, body_pts=body_pts, body_names=body_names_list, body_dist=body_dist, body_diampts=use_diampts, update_names=False, mindist=0.0, pmerids=pmerids, plats=plats, mmerids=mmerids, mlats=mlats, lit_line=NO_LINE, dark_line=NO_LINE, term_line=NO_LINE, lit_line2=NO_LINE, dark_line2=NO_LINE, term_line2=NO_LINE, prime_pts=0.0, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) # Interior rings _rspk_draw_rings( iring1=1, iring2=last_opaq, ring_flags=ring_flags, ring_locs=r_ring_locs, ring_axes1=r_ring_axes1, ring_axes2=r_ring_axes2, ring_dark=r_ring_dark, ring_dashed=ring_dashed, nloops=nloops, loop_locs=loop_locs, loop_axes1=loop_axes1, loop_axes2=loop_axes2, loop_ring=loop_ring, arc_width=use_arcpts, lit_line=LIT_LINE, dark_line=DARK_LINE, shadow_line=SHADOW_LINE, term_line=term_line, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) else: # Case 2: Opaque lo = last_opaq - 1 ext_locs = [*body_locs, r_ring_locs[lo]] ext_axes = [*body_axes, [r_ring_axes1[lo], r_ring_axes2[lo], r_ring_axes3[lo]]] eugeom( 1, [sun_loc], [sun_rad], obs_pv[:3], [cmat[0], cmat[1], cmat[2]], nbodies + 1, ext_locs, ext_axes, euclid_state, ) _rspk_draw_bodies( nbodies=nbodies, body_pts=body_pts, body_names=body_names_list, body_dist=body_dist, body_diampts=use_diampts, update_names=True, mindist=ring_rads[lo] if lo < len(ring_rads) else 0.0, pmerids=pmerids, plats=plats, mmerids=mmerids, mlats=mlats, lit_line=LIT_LINE, dark_line=DARK_LINE, term_line=term_line, lit_line2=NO_LINE, dark_line2=NO_LINE, term_line2=NO_LINE, prime_pts=options.prime_pts, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) eubody( nbodies + 1, 0, 0, 1, NO_LINE, NO_LINE, NO_LINE, euclid_state, view_state, escher_state ) _rspk_draw_rings( iring1=last_opaq + 1, iring2=use_nrings, ring_flags=ring_flags, ring_locs=r_ring_locs, ring_axes1=r_ring_axes1, ring_axes2=r_ring_axes2, ring_dark=r_ring_dark, ring_dashed=ring_dashed, nloops=nloops, loop_locs=loop_locs, loop_axes1=loop_axes1, loop_axes2=loop_axes2, loop_ring=loop_ring, arc_width=use_arcpts, lit_line=LIT_LINE, dark_line=DARK_LINE, shadow_line=SHADOW_LINE, term_line=term_line, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) # Re-define without rings eugeom( 1, [sun_loc], [sun_rad], obs_pv[:3], [cmat[0], cmat[1], cmat[2]], nbodies, body_locs, body_axes, euclid_state, ) # Re-draw interior moons _rspk_draw_bodies( nbodies=nbodies, body_pts=body_pts, body_names=body_names_list, body_dist=body_dist, body_diampts=use_diampts, update_names=True, mindist=ring_rads[lo] if lo < len(ring_rads) else 0.0, pmerids=pmerids, plats=plats, mmerids=mmerids, mlats=mlats, lit_line=NO_LINE, dark_line=NO_LINE, term_line=NO_LINE, lit_line2=LIT_LINE, dark_line2=DARK_LINE, term_line2=term_line, prime_pts=options.prime_pts, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) # Interior rings _rspk_draw_rings( iring1=1, iring2=last_opaq, ring_flags=ring_flags, ring_locs=r_ring_locs, ring_axes1=r_ring_axes1, ring_axes2=r_ring_axes2, ring_dark=r_ring_dark, ring_dashed=ring_dashed, nloops=nloops, loop_locs=loop_locs, loop_axes1=loop_axes1, loop_axes2=loop_axes2, loop_ring=loop_ring, arc_width=use_arcpts, lit_line=LIT_LINE, dark_line=DARK_LINE, shadow_line=SHADOW_LINE, term_line=term_line, euclid_state=euclid_state, view_state=view_state, escher_state=escher_state, ) draw_view_box_labels_stars_close( escher_state=escher_state, view_state=view_state, euclid_state=euclid_state, cmat=cmat, delta=delta, moon_labelpts=options.moon_labelpts, body_names_list=body_names_list, body_los=body_los, body_pts=body_pts, use_diampts=use_diampts, fov=options.fov, nbodies=nbodies, nstars=options.nstars, star_ras=star_ras, star_decs=star_decs, star_names=star_names, star_labels=options.star_labels, star_diampts=options.star_diampts, )