1d7f3e2136
Testy / Build obrazu silnika B (swisseph) (push) Waiting to run
Testy / Kontrola składni wszystkich warstw (push) Waiting to run
build / build (push) Successful in 1m26s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m10s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Has started running
Pierwszy krok rysowania wykresu (PRE-12): SZKIELET koła — pierścień znaków, podział na domy i osie. Planety i linie aspektów w kolejnych iteracjach. Rysowane po stronie serwera jako czysty string SVG (bez zależności zewnętrznych — spójne z CSP). Ciemny motyw, spójny z paletą aplikacji (kolory z --line, --accent, --muted; glify znaków barwione wg żywiołu w stonowanych kolorach czytelnych na ciemnym tle). Geometria wg konwencji astrologicznej: Ascendent po LEWEJ, długość ekliptyczna rośnie przeciwnie do ruchu wskazówek zegara. Punkt λ → kąt φ = 180° − (λ − Asc); w SVG y rośnie w dół, co formuła uwzględnia. Zakotwiczenie sprawdzone testami liczbowo: Asc po lewej, Dsc po prawej, oś pozioma; Asc+90° (II dom) na dole. Elementy: dwa okręgi + piasta, podziałki co 5°/10°, granice znaków co 30° z glifem w środku sektora, szprychy domów od pasa do piasty, numery domów w środku każdego domu, osie Asc–Dsc i MC–IC wyróżnione akcentem z etykietami AC/DC/MC/IC. Dane bierzemy WYŁĄCZNIE z wyniku /chart/positions — prezentacja nic nie liczy: - `sign_glyphs` (pierścień 12 znaków) i glify — z LOG-22, - `angles` z długością `decimal` — już były, - `cusps` z `decimal` — DOŁOŻONE w tym PR (jedna linia w build_chart). Bez tego domy dało się narysować tylko dla whole sign; z długością cuspu działa dla KAŻDEGO systemu. Zweryfikowane na porphyry: cuspy poza wielokrotnościami 30°, szprychy odchodzą od granic znaków. Degradacja: silnik bez osi/domów albo starszy wynik bez `decimal` w cuspach → brak koła (pusty string), nie wyjątek. Testy: 8 (poprawność XML, 12 glifów, 4 osie, numery domów, Asc po lewej liczbowo, CCW, degradacja). Całość: prezentacja 33 passed, logika 265 / 1 skipped. Render potwierdzony wizualnie na horoskopie referencyjnym (AC=Leo po lewej, MC=Aries u góry, domy CCW). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
93 lines
3.5 KiB
Python
93 lines
3.5 KiB
Python
"""Szkielet kosmogramu — SVG po stronie serwera (PRE-12).
|
||
|
||
Sprawdzamy, że SVG jest POPRAWNY (parsuje się jako XML — inaczej wstawiony do
|
||
strony rozjedzie render) i że niesie to, co ma nieść: pierścień 12 znaków, cztery
|
||
osie i numery domów. Geometria (Asc po lewej) sprawdzona liczbowo, nie na oko.
|
||
"""
|
||
import xml.dom.minidom as minidom
|
||
|
||
from app import chartwheel
|
||
|
||
|
||
def _sample_chart(asc=128.9, mc=5.0):
|
||
"""Minimalny wynik z warstwy logiki: osie + cuspy (whole sign) + pierścień znaków."""
|
||
signs = ["Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
|
||
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"]
|
||
glyphs = ["♈", "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓"]
|
||
asc_sign = int(asc // 30)
|
||
cusps = [{"house": i + 1, "sign": signs[(asc_sign + i) % 12],
|
||
"decimal": (asc_sign * 30 + i * 30) % 360, "sign_glyph": glyphs[(asc_sign + i) % 12]}
|
||
for i in range(12)]
|
||
return {
|
||
"angles": {
|
||
"Asc": {"name": "Asc", "decimal": asc, "sign": signs[asc_sign]},
|
||
"MC": {"name": "MC", "decimal": mc},
|
||
"Dsc": {"name": "Dsc", "decimal": (asc + 180) % 360},
|
||
"IC": {"name": "IC", "decimal": (mc + 180) % 360},
|
||
},
|
||
"cusps": cusps,
|
||
"sign_glyphs": [{"sign": s, "glyph": g} for s, g in zip(signs, glyphs)],
|
||
}
|
||
|
||
|
||
def _rendered():
|
||
return chartwheel.render(_sample_chart())
|
||
|
||
|
||
def test_output_is_well_formed_xml():
|
||
"""Wstawiamy surowy SVG do strony — musi być poprawnym XML, bo inaczej
|
||
zepsuje cały dokument."""
|
||
svg = _rendered().replace("var(--line)", "#000").replace(
|
||
"var(--accent)", "#000").replace("var(--muted)", "#000")
|
||
doc = minidom.parseString(svg) # rzuci wyjątkiem, gdy błędny
|
||
assert doc.documentElement.tagName == "svg"
|
||
|
||
|
||
def test_has_twelve_sign_glyphs():
|
||
assert _rendered().count('class="glyph"') == 12
|
||
|
||
|
||
def test_has_all_four_axis_labels():
|
||
svg = _rendered()
|
||
for label in (">AC<", ">DC<", ">MC<", ">IC<"):
|
||
assert label in svg
|
||
|
||
|
||
def test_has_house_numbers_one_to_twelve():
|
||
svg = _rendered()
|
||
for h in range(1, 13):
|
||
assert f">{h}<" in svg
|
||
|
||
|
||
def test_ascendant_is_on_the_left():
|
||
"""Konwencja: Asc po lewej. Sprawdzamy oś AC–DC liczbowo — punkt Asc musi mieć
|
||
x wyraźnie mniejszy od środka (220), a Dsc większy."""
|
||
chart = _sample_chart(asc=128.9)
|
||
asc = chart["angles"]["Asc"]["decimal"]
|
||
ax, ay = chartwheel._pt(asc, asc, chartwheel.R_ZOD)
|
||
dx, dy = chartwheel._pt(asc + 180, asc, chartwheel.R_ZOD)
|
||
assert ax < chartwheel._CX - 100, "Asc powinien być po lewej"
|
||
assert dx > chartwheel._CX + 100, "Dsc powinien być po prawej"
|
||
assert abs(ay - chartwheel._CY) < 1e-6, "oś Asc–Dsc jest pozioma"
|
||
|
||
|
||
def test_longitude_increases_counterclockwise():
|
||
"""Asc+90° (II dom) ląduje na DOLE koła (y > środek, bo w SVG y rośnie w dół)."""
|
||
asc = 100.0
|
||
_, y = chartwheel._pt(asc + 90, asc, chartwheel.R_ZOD)
|
||
assert y > chartwheel._CY + 100
|
||
|
||
|
||
def test_empty_when_no_angles():
|
||
"""Silnik bez osi/domów (brak sidereal) → brak koła, nie wyjątek."""
|
||
assert chartwheel.render({"positions": []}) == ""
|
||
assert chartwheel.render({"angles": {}, "cusps": [], "sign_glyphs": []}) == ""
|
||
|
||
|
||
def test_empty_when_cusps_lack_longitude():
|
||
"""Starsze wyniki bez `decimal` w cuspach — degradujemy do pustego, nie sypiemy."""
|
||
chart = _sample_chart()
|
||
for c in chart["cusps"]:
|
||
del c["decimal"]
|
||
assert chartwheel.render(chart) == ""
|