feat(prezentacja): wykres deklinacji i oś antyscji (LOG-07, kosmogram etap 6)
Ostatni etap rysowania kosmogramu: wizualizacje pochodne z aspektów pozazodiakalnych (LOG-07). Do tej pory paralele i antyscja były tylko w tabelach — teraz widać je na rzut oka. WYKRES DEKLINACJI (`render_declination`): - Pionowa skala deklinacji z równikiem (0°) i zwrotnikami (±ε, kreskowane) — ε bierzemy z `obliquity` w wyniku, więc granica jest dokładna dla daty. - Obiekty na osi X ułożone wg POSORTOWANEJ deklinacji, więc paralele (ta sama wysokość) lądują obok siebie. Łączniki: paralela zielona (jak koniunkcja), kontrparalela czerwona (jak opozycja) — te same barwy co linie na kole, grubość wg orbu. Dymek z nazwą zjawiska i orbem. - Strefa poza zwrotnikami cieniowana; obiekt OOB (out-of-bounds) w kolorze wyróżnienia + „OOB" w dymku. Od razu widać ciała o skrajnej deklinacji. OŚ ANTYSCJI (`render_antiscia`): - Ekliptyka rozwinięta w poziomą oś ze znakami; pionowo zaznaczona oś przesileń (0° Raka/Koziorożca) — lustro antyscji — i oś równonocy (0° Barana/Wagi) dla kontrantyscji. Pary połączone łukiem (zielony antyscja / czerwony kontrantyscja), z dymkiem. Bez par oś i obiekty i tak coś mówią. Oba w obu motywach (screen + print), więc gotowe też do PDF-a. Pokazują się na /chart pod odpowiednimi tabelami LOG-07. Testy: +13 (etap 6). Prezentacja: 170. Domyka etapy kosmogramu (PRE-12): 1 szkielet, 2 obiekty, 3 aspekty, 4 dopracowanie, 5 aspektarian, 6 deklinacja/antyscja. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -429,3 +429,117 @@ def test_aspectarian_print_theme_has_no_css_vars():
|
||||
svg = chartwheel.render_aspectarian(_chart_with_aspects(), theme="print")
|
||||
assert "var(--" not in svg
|
||||
assert "#a83232" in svg # square napięty w wariancie druku
|
||||
|
||||
|
||||
# ─────────────── etap 6: deklinacja i antyscja (LOG-07) ───────────────────
|
||||
|
||||
def _chart_with_declination():
|
||||
chart = _chart_with_objects() # Sun/Moon/Venus/Mercury
|
||||
chart["obliquity"] = 23.44
|
||||
decs = {"Sun": 15.0, "Moon": 15.2, "Venus": -15.0, "Mercury": 25.5}
|
||||
for p in chart["positions"]:
|
||||
p["declination"] = decs[p["name"]]
|
||||
if p["name"] == "Mercury":
|
||||
p["out_of_bounds"] = True # 25.5° > ε=23.44 → OOB
|
||||
chart["parallels"] = [
|
||||
{"obj1": "Sun", "obj2": "Moon", "type": "parallel",
|
||||
"orb": 0.2, "allowed": 1.0, "dec1": 15.0, "dec2": 15.2},
|
||||
{"obj1": "Sun", "obj2": "Venus", "type": "contraparallel",
|
||||
"orb": 0.0, "allowed": 1.0, "dec1": 15.0, "dec2": -15.0},
|
||||
]
|
||||
return chart
|
||||
|
||||
|
||||
def test_declination_is_well_formed_xml():
|
||||
svg = chartwheel.render_declination(_chart_with_declination())
|
||||
svg = (svg.replace("var(--line)", "#000").replace("var(--accent)", "#000")
|
||||
.replace("var(--muted)", "#000").replace("var(--ink)", "#000"))
|
||||
assert minidom.parseString(svg).documentElement.tagName == "svg"
|
||||
|
||||
|
||||
def test_declination_empty_for_fewer_than_two():
|
||||
chart = _chart_with_declination()
|
||||
chart["positions"] = chart["positions"][:1]
|
||||
assert chartwheel.render_declination(chart) == ""
|
||||
|
||||
|
||||
def test_declination_draws_equator_and_tropics():
|
||||
"""Równik (0°) i dwie linie zwrotników (kreskowane) muszą być."""
|
||||
svg = chartwheel.render_declination(_chart_with_declination())
|
||||
assert ">0°<" in svg
|
||||
assert svg.count("stroke-dasharray") >= 2 # ±ε kreskowane
|
||||
|
||||
|
||||
def test_declination_object_glyphs_present():
|
||||
svg = chartwheel.render_declination(_chart_with_declination())
|
||||
for g in ("☉", "☽", "♀", "☿"):
|
||||
assert g in svg
|
||||
|
||||
|
||||
def test_declination_oob_object_is_highlighted():
|
||||
"""Merkury poza zwrotnikami (OOB) w kolorze wyróżnienia + z „OOB" w dymku."""
|
||||
svg = chartwheel.render_declination(_chart_with_declination())
|
||||
assert "#ff9b6a" in svg # kolor retro/OOB (motyw ekranowy)
|
||||
assert "OOB" in svg
|
||||
|
||||
|
||||
def test_declination_parallel_and_contraparallel_colors():
|
||||
"""Paralela jak koniunkcja (zielony), kontrparalela jak opozycja (czerwony)."""
|
||||
svg = chartwheel.render_declination(_chart_with_declination())
|
||||
assert "#8fae7a" in svg # parallel → zielony
|
||||
assert "#cf6a6a" in svg # contraparallel → czerwony
|
||||
assert "paralela" in svg # dymek
|
||||
|
||||
|
||||
def test_declination_print_theme_has_no_css_vars():
|
||||
svg = chartwheel.render_declination(_chart_with_declination(), theme="print")
|
||||
assert "var(--" not in svg
|
||||
|
||||
|
||||
def _chart_with_antiscia():
|
||||
chart = _chart_with_objects()
|
||||
chart["antiscia"] = [
|
||||
{"obj1": "Sun", "obj2": "Moon", "type": "antiscion", "orb": 0.3, "allowed": 1.0},
|
||||
{"obj1": "Venus", "obj2": "Mercury", "type": "contra_antiscion", "orb": 0.5, "allowed": 1.0},
|
||||
]
|
||||
return chart
|
||||
|
||||
|
||||
def test_antiscia_is_well_formed_xml():
|
||||
svg = chartwheel.render_antiscia(_chart_with_antiscia())
|
||||
svg = (svg.replace("var(--line)", "#000").replace("var(--accent)", "#000")
|
||||
.replace("var(--muted)", "#000").replace("var(--ink)", "#000"))
|
||||
assert minidom.parseString(svg).documentElement.tagName == "svg"
|
||||
|
||||
|
||||
def test_antiscia_empty_for_fewer_than_two():
|
||||
chart = _chart_with_antiscia()
|
||||
chart["positions"] = chart["positions"][:1]
|
||||
assert chartwheel.render_antiscia(chart) == ""
|
||||
|
||||
|
||||
def test_antiscia_draws_reflection_axes():
|
||||
"""Oś przesileń (i równonocy) — linie kreskowane."""
|
||||
svg = chartwheel.render_antiscia(_chart_with_antiscia())
|
||||
assert svg.count("stroke-dasharray") >= 2
|
||||
|
||||
|
||||
def test_antiscia_pair_colors_and_tooltip():
|
||||
"""Antyscja jak koniunkcja (zielony łuk), kontrantyscja jak opozycja (czerwony)."""
|
||||
svg = chartwheel.render_antiscia(_chart_with_antiscia())
|
||||
assert "#8fae7a" in svg # antiscion → zielony
|
||||
assert "#cf6a6a" in svg # contra_antiscion → czerwony
|
||||
assert "antyscja" in svg and "<path" in svg
|
||||
|
||||
|
||||
def test_antiscia_renders_axis_even_without_pairs():
|
||||
"""Bez par (ciasny orb) oś i obiekty i tak coś mówią."""
|
||||
chart = _chart_with_objects()
|
||||
chart["antiscia"] = []
|
||||
svg = chartwheel.render_antiscia(chart)
|
||||
assert svg and "stroke-dasharray" in svg and "☉" in svg
|
||||
|
||||
|
||||
def test_antiscia_print_theme_has_no_css_vars():
|
||||
svg = chartwheel.render_antiscia(_chart_with_antiscia(), theme="print")
|
||||
assert "var(--" not in svg
|
||||
|
||||
Reference in New Issue
Block a user