Source code for ephemeris_tools.rendering.euclid.ring

"""Draw ring ellipse (EURING)."""

from __future__ import annotations

import math

from ephemeris_tools.rendering.escher import (
    EscherState,
    EscherViewState,
    esdraw,
    esdump,
)
from ephemeris_tools.rendering.euclid.constants import LIMFOV, STDSEG
from ephemeris_tools.rendering.euclid.ellipse import _ovrlap
from ephemeris_tools.rendering.euclid.init_geom import euinit
from ephemeris_tools.rendering.euclid.segment_plane import (
    _euskip,
    _fovclp,
    _plelsg,
    _smside,
)
from ephemeris_tools.rendering.euclid.state import EuclidState
from ephemeris_tools.rendering.euclid.vec_math import (
    Vec3,
    _mtxv,
    _v3t,
    _vadd,
    _vequ,
    _vlcom,
    _vnorm,
    _vscl,
    _vsub,
)


[docs] def euring( ring_center: Vec3, major: Vec3, minor: Vec3, srcreq: int, bright: int, dark: int, euclid_state: EuclidState, view_state: EscherViewState, escher_state: EscherState, ) -> None: """Draw an elliptical ring (port of EURING). Ring is a curve only; it does not cast shadows. Lit and dark portions use the given color codes. MAJOR and MINOR are assumed orthogonal (semi-axes). Parameters: ring_center: Center of the ring (e.g. planet center). major: Semi-major axis vector (direction and length). minor: Semi-minor axis vector (direction and length). srcreq: Number of light sources required to consider a point lit. bright: Color code for lit portion. dark: Color code for unlit portion. euclid_state: Euclid state (from eugeom/euview). view_state: Escher view state. escher_state: Escher output state. """ st = euclid_state if not st.initialized: euinit(st) # Transform ring to camera frame rcentr = _vsub(ring_center, st.obsrvr) rcentr = _mtxv(st.camera, rcentr) rmajor = _mtxv(st.camera, major) rminor = _mtxv(st.camera, minor) largst = _vnorm(rmajor) intsec = _ovrlap(rcentr, largst, st.fovcen, st.fovrad) if intsec == 0: return # Find candidate occulting bodies occltd = False ringd = _vnorm(rcentr) fringd = ringd + largst nringd = ringd - largst x = largst / _vnorm(rcentr) if _vnorm(rcentr) > 0 else 1.0 x = 1.0 - x * x occrng = _vscl(x, rcentr) nocand = 0 ocands_r: list[int] = [] i = 0 while i < st.nbody and not occltd: near = _vnorm(st.centrs[i]) - st.biga[i] if st.cansee[i] and near < fringd: if x <= 0.0: ocands_r.append(i) nocand += 1 else: intsec = _ovrlap(occrng, largst, st.lcentr[i], st.biga[i]) if intsec > 1: ocands_r.append(i) nocand += 1 elif intsec == 1: ocands_r.append(i) nocand += 1 if _vnorm(st.centrs[i]) + st.smalla[i] < nringd: intsec2 = _ovrlap( occrng, largst, st.centrs[i], st.smalla[i], ) occltd = intsec2 == 1 i += 1 if occltd: return # NOVIEW check if x < 0.0: noview = True else: intsec = _ovrlap(rcentr, largst, st.kaxis, math.tan(LIMFOV)) noview = intsec != 1 # Eclipse candidates drkreq = 1 + st.nlight - srcreq npsecl = 0 ndfecl = 0 necand_r: list[int] = [0] * st.nlight ecands_r: list[list[int]] = [[] for _ in range(st.nlight)] eclpsd_r: list[bool] = [False] * st.nlight for j in range(st.nlight): srcrng = _vsub(rcentr, st.lights[j]) ringd_j = _vnorm(srcrng) fringd_j = ringd_j + largst nringd_j = ringd_j - largst necand_r[j] = 0 eclpsd_r[j] = False i = 0 while i < st.nbody and not eclpsd_r[j]: canbod = _vsub(st.centrs[i], st.lights[j]) if st.canecl[i][j] and _vnorm(canbod) - st.biga[i] < fringd_j: canbod2 = _vsub(st.tcentr[i][j], st.vertex[i][j]) eclrng = _vsub(rcentr, st.vertex[i][j]) xv = largst / _vnorm(eclrng) if _vnorm(eclrng) > 0 else 1.0 xv = 1.0 - xv * xv if xv <= 0.0: ecands_r[j].append(i) necand_r[j] += 1 else: eclrng_s = _vscl(xv, eclrng) intsec = _ovrlap(eclrng_s, largst, canbod2, st.biga[i]) if intsec > 1: ecands_r[j].append(i) necand_r[j] += 1 elif intsec == 1: ecands_r[j].append(i) necand_r[j] += 1 canbod3 = _vsub(st.centrs[i], st.lights[j]) if _vnorm(canbod3) + st.smalla[i] < nringd_j: canbod4 = _vsub(st.centrs[i], st.vertex[i][j]) intsec2 = _ovrlap(eclrng_s, largst, canbod4, st.smalla[i]) eclpsd_r[j] = intsec2 == 1 i += 1 if necand_r[j] > 0: npsecl += 1 if eclpsd_r[j]: ndfecl += 1 # Generate ring segments skip = _euskip(largst, rcentr, st.fovrad) begseg_list: list[Vec3] = [_vadd(rcentr, rmajor)] endseg_list: list[Vec3] = [] nxtstd = skip segno = 0 while nxtstd < STDSEG: cosang_v = st.stdcos[nxtstd] sinang_v = st.stdsin[nxtstd] endpt = _vadd(rcentr, _vlcom(cosang_v, rmajor, sinang_v, rminor)) endseg_list.append(endpt) segno += 1 begseg_list.append(_vequ(endpt)) nxtstd += skip # Close the ring endseg_list.append(_vadd(rcentr, rmajor)) segno += 1 numseg = segno # Remove extra begin entry begseg_list = begseg_list[:numseg] # Check occultation vupnt_occ: Vec3 = [0.0, 0.0, 0.0] kept_beg: list[Vec3] = [] kept_end: list[Vec3] = [] si = 0 while si < len(begseg_list): bc = _vequ(begseg_list[si]) ec = _vequ(endseg_list[si]) savseg = True oi = 0 while oi < nocand and savseg: j_occ = ocands_r[oi] bsub, esub, ins, inb, ns = _plelsg( bc, ec, st.lnorml[j_occ], st.lmajor[j_occ], st.lminor[j_occ], st.lcentr[j_occ], vupnt_occ, ) sub = 0 while sub < ns and inb[sub] and ins[sub]: sub += 1 if sub < ns: bc = _vequ(bsub[sub]) ec = _vequ(esub[sub]) sub += 1 else: savseg = False while sub < ns: if not (inb[sub] and ins[sub]): begseg_list.append(_vequ(bsub[sub])) endseg_list.append(_vequ(esub[sub])) sub += 1 oi += 1 if savseg: kept_beg.append(bc) kept_end.append(ec) si += 1 begseg_list = kept_beg endseg_list = kept_end numseg = len(begseg_list) # Eclipse checks if ndfecl >= drkreq: for si in range(numseg): bc = begseg_list[si] ec = endseg_list[si] if noview: bc, ec = _fovclp(bc, ec, st.cosfov) esdraw(_v3t(bc), _v3t(ec), dark, view_state, escher_state) numseg = 0 if npsecl == 0 and numseg > 0: for si in range(numseg): bc = begseg_list[si] ec = endseg_list[si] if noview: bc, ec = _fovclp(bc, ec, st.cosfov) esdraw(_v3t(bc), _v3t(ec), bright, view_state, escher_state) numseg = 0 # Per-segment eclipse check si = 0 while si < numseg: bc = _vequ(begseg_list[si]) ec = _vequ(endseg_list[si]) lsrce = 0 nillum = 0 ndark_v = 0 notecl = True notlit = True while lsrce < st.nlight and notecl and notlit: curdrk = ndark_v unknwn2 = True j2 = 0 notecl = ndark_v < drkreq notlit = nillum < srcreq unknwn2 = unknwn2 and notecl and notlit and (j2 < necand_r[lsrce]) while unknwn2: k = ecands_r[lsrce][j2] bsub, esub, ins, inb, ns = _plelsg( bc, ec, st.tnorml[k][lsrce], st.tmajor[k][lsrce], st.tminor[k][lsrce], st.tcentr[k][lsrce], st.vertex[k][lsrce], ) if ns > 1: bc = _vequ(bsub[0]) ec = _vequ(esub[0]) for sub in range(1, ns): begseg_list.append(_vequ(bsub[sub])) endseg_list.append(_vequ(esub[sub])) numseg += 1 if ( ns >= 1 and ins[0] and not _smside( bc, ec, st.tnorml[k][lsrce], st.tcentr[k][lsrce], st.lights[lsrce] ) ): ndark_v += 1 unknwn2 = False j2 += 1 unknwn2 = unknwn2 and (j2 < necand_r[lsrce]) notecl = ndark_v < drkreq if curdrk == ndark_v: nillum += 1 notlit = nillum < srcreq lsrce += 1 if notecl: if noview: bc, ec = _fovclp(bc, ec, st.cosfov) esdraw(_v3t(bc), _v3t(ec), bright, view_state, escher_state) else: if noview: bc, ec = _fovclp(bc, ec, st.cosfov) esdraw(_v3t(bc), _v3t(ec), dark, view_state, escher_state) si += 1 esdump(view_state, escher_state)