feat(prezentacja): aspektarian — siatka aspektów (PRE-18, kosmogram etap 5)
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m37s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m32s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 25s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 19s
build / build (push) Successful in 48s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m34s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 10m2s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 1m7s
Testy / Kontrola składni wszystkich warstw (push) Successful in 41s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m37s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m32s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 25s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 19s
build / build (push) Successful in 48s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m34s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 10m2s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 1m7s
Testy / Kontrola składni wszystkich warstw (push) Successful in 41s
Trójkątna siatka aspektów obiekt×obiekt obok koła. Koło pokazuje GEOMETRIĘ aspektów (linie w środku), aspektarian domyka temat od drugiej strony — TABELĘ na jeden rzut oka: kto z kim i jak. Klasyczny „schodkowy" układ: glify obiektów biegną po przekątnej, a każda komórka pod nią to aspekt między obiektem ze swojego wiersza a obiektem ze swojej kolumny. Pusta komórka też niesie informację — że pary NIC nie łączy. Spójność z kołem trzymana świadomie: - te same barwy aspektów (niebieski harmonijny / czerwony napięty / zielony koniunkcja) — oko łapie ten sam kod na kole i w siatce; - ciasny aspekt (orb <1°) pogrubiony, tak jak grubsza linia na kole; - natywne dymki <title> (bez JS): „Słońce trygon Mars · orb 0.20° · aplikacyjny", polskie nazwy tylko w dymku — obliczenia trzymają angielskie; - oba motywy: „screen" (zmienne CSS aplikacji) i „print" (konkretne kolory + font glifów wprost), więc siatka jest gotowa też do PDF-a. Glify aspektów (☌ ☍ △ □ ⚹ ⚺ ⚻ ∠) trzymamy lokalnie w prezentacji — tam gdzie już są kolory i polskie nazwy — więc aspektarian jest samowystarczalny i nie zależy od tego, czy pojedynczy rekord aspektu niesie glif. Renderer degraduje się do pustego przy mniej niż dwóch obiektach; niekompletny obiekt (bez glifu) go nie wywala. Aspektarian pokazuje się na /chart i /compile pod kołem. Testy: +12 (etap 5). Całość prezentacji: 157. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #38.
This commit is contained in:
@@ -337,3 +337,95 @@ def test_print_theme_survives_stage4_features():
|
||||
svg = chartwheel.render(chart, theme="print")
|
||||
assert "var(--" not in svg
|
||||
assert "⊗" in svg and "<title>" in svg
|
||||
|
||||
|
||||
# ─────────────────────────── etap 5: aspektarian (PRE-18) ─────────────────
|
||||
|
||||
def test_aspectarian_is_well_formed_xml():
|
||||
"""Aspektarian też wstawiamy surowo do strony — musi być poprawnym XML."""
|
||||
svg = chartwheel.render_aspectarian(_chart_with_aspects())
|
||||
svg = (svg.replace("var(--line)", "#000").replace("var(--accent)", "#000")
|
||||
.replace("var(--muted)", "#000").replace("var(--ink)", "#000"))
|
||||
doc = minidom.parseString(svg)
|
||||
assert doc.documentElement.tagName == "svg"
|
||||
|
||||
|
||||
def test_aspectarian_triangular_grid_has_right_cell_count():
|
||||
"""N obiektów → przekątna (N komórek) + dolny-lewy trójkąt (N·(N−1)/2).
|
||||
Fixture ma 5 obiektów: 5 + 10 = 15 komórek."""
|
||||
svg = chartwheel.render_aspectarian(_chart_with_aspects())
|
||||
assert svg.count("<rect") == 15
|
||||
|
||||
|
||||
def test_aspectarian_puts_object_glyphs_on_diagonal():
|
||||
"""Każdy obiekt raz — na przekątnej."""
|
||||
svg = chartwheel.render_aspectarian(_chart_with_aspects())
|
||||
for glyph in ("☉", "☽", "♀", "☿", "♂"):
|
||||
assert glyph in svg
|
||||
|
||||
|
||||
def test_aspectarian_shows_aspect_glyphs():
|
||||
"""Aspekty z fixture: trygon (△), kwadratura (□), koniunkcja (☌)."""
|
||||
svg = chartwheel.render_aspectarian(_chart_with_aspects())
|
||||
assert "△" in svg and "□" in svg and "☌" in svg
|
||||
|
||||
|
||||
def test_aspectarian_colors_match_the_wheel():
|
||||
"""Te same barwy co linie na kole: niebieski/czerwony/zielony."""
|
||||
svg = chartwheel.render_aspectarian(_chart_with_aspects())
|
||||
assert "#6aa6c9" in svg # trine — harmonijny
|
||||
assert "#cf6a6a" in svg # square — napięty
|
||||
assert "#8fae7a" in svg # conjunction — zielony
|
||||
|
||||
|
||||
def test_aspectarian_tight_aspect_is_bold():
|
||||
"""Orb < 1° → glif pogrubiony (jak pogrubiona linia na kole). Dwa ciasne
|
||||
aspekty w fixture (Sun–Mars 0.2°, Mercury–Venus 0.4°) → dwa pogrubienia."""
|
||||
svg = chartwheel.render_aspectarian(_chart_with_aspects())
|
||||
assert svg.count('font-weight="bold"') == 2
|
||||
|
||||
|
||||
def test_aspectarian_cells_have_tooltips():
|
||||
svg = chartwheel.render_aspectarian(_chart_with_aspects())
|
||||
assert "<title>" in svg
|
||||
assert "trygon" in svg and "orb" in svg
|
||||
|
||||
|
||||
def test_aspectarian_tooltip_shows_applying_separating():
|
||||
"""Gdy aspekt niesie kierunek (A/S), dymek to mówi po polsku."""
|
||||
chart = _chart_with_aspects()
|
||||
chart["aspects"][0]["as"] = "A" # Sun–Mars aplikacyjny
|
||||
svg = chartwheel.render_aspectarian(chart)
|
||||
assert "aplikacyjny" in svg
|
||||
|
||||
|
||||
def test_aspectarian_empty_for_fewer_than_two_objects():
|
||||
chart = _chart_with_objects()
|
||||
chart["positions"] = chart["positions"][:1]
|
||||
assert chartwheel.render_aspectarian(chart) == ""
|
||||
|
||||
|
||||
def test_aspectarian_renders_grid_even_without_aspects():
|
||||
"""Pusta siatka też niesie informację (które pary NIE mają aspektu) —
|
||||
rysujemy przekątną z obiektami, bez glifów aspektów."""
|
||||
chart = _chart_with_objects()
|
||||
chart["aspects"] = []
|
||||
svg = chartwheel.render_aspectarian(chart)
|
||||
assert svg and "☉" in svg
|
||||
for asp_glyph in ("△", "□", "☌", "⚹", "☍"):
|
||||
assert asp_glyph not in svg
|
||||
|
||||
|
||||
def test_aspectarian_object_without_glyph_is_safe():
|
||||
"""Niekompletny obiekt (bez glifu) nie wywala siatki."""
|
||||
chart = _chart_with_aspects()
|
||||
chart["positions"].append({"name": "Chiron", "decimal": 100.0}) # brak glyph
|
||||
svg = chartwheel.render_aspectarian(chart)
|
||||
assert svg and "☉" in svg
|
||||
|
||||
|
||||
def test_aspectarian_print_theme_has_no_css_vars():
|
||||
"""Motyw druku (PDF) — konkretne kolory, żaden `var(--…)`."""
|
||||
svg = chartwheel.render_aspectarian(_chart_with_aspects(), theme="print")
|
||||
assert "var(--" not in svg
|
||||
assert "#a83232" in svg # square napięty w wariancie druku
|
||||
|
||||
Reference in New Issue
Block a user