mirror of
https://github.com/migatu/astrololo.git
synced 2026-07-14 21:38:37 +00:00
Aspekty: applying/separating (A/S) + bonus siły dla aplikujących (LOG-06)
- aspects.py: _is_applying — odchyłka od dokładnego kąta teraz vs po małym
kroku czasu (prędkość·dt, dt=0,01 doby by szybki Księżyc nie przeskoczył
dokładności); aspekt niesie applying (bool) i "as": A/S. Bez prędkości —
brak flagi (None).
- scoring: aspekt aplikacyjny silniejszy (APPLYING_BONUS 1.15) — zgodnie z
notatkami projektu ("impact considered more powerful").
- significators: faseta aspektu niesie applying, etykieta z sufiksem (A)/(S).
- widok Horoskop: kolumna A/S w tabeli aspektów (tooltip z objaśnieniem).
Walidacja: flagi A/S wszystkich 16 aspektów horoskopu referencyjnego
(30.04.1984, Warszawa) zgodne z tabelą astro-seek z notes3 (test regresyjny
REFERENCE_AS). 45 testów przechodzi.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -34,10 +34,29 @@ def separation(a: float, b: float) -> float:
|
||||
return min(d, 360.0 - d)
|
||||
|
||||
|
||||
def _is_applying(la: float, lb: float, sa: float, sb: float, angle: float, dt: float = 0.01) -> bool | None:
|
||||
"""Czy aspekt aplikuje (dokładność 0° dopiero nastąpi)?
|
||||
|
||||
Porównujemy odchyłkę od dokładnego kąta teraz i po małym kroku czasu
|
||||
(pozycje przesunięte o prędkość·dt). Malejąca odchyłka = applying.
|
||||
dt celowo małe (0,01 doby), by szybki Księżyc nie „przeskoczył" dokładności.
|
||||
Zwraca None, gdy brak prędkości (nie da się rozstrzygnąć).
|
||||
"""
|
||||
if sa is None or sb is None:
|
||||
return None
|
||||
dev_now = abs(separation(la, lb) - angle)
|
||||
dev_next = abs(separation(la + sa * dt, lb + sb * dt) - angle)
|
||||
return dev_next < dev_now
|
||||
|
||||
|
||||
def find_aspects(
|
||||
positions: list[dict], orb: float = DEFAULT_ORB, luminary_bonus: float = LUMINARY_BONUS
|
||||
) -> list[dict]:
|
||||
"""positions: dicty z 'name' i 'decimal' (długość). Zwraca listę aspektów."""
|
||||
"""positions: dicty z 'name', 'decimal' (długość) i opcjonalnie 'speed' (°/dobę).
|
||||
|
||||
Zwraca listę aspektów głównych; gdy znane są prędkości, każdy aspekt ma
|
||||
applying (bool) i skrót 'as': 'A'/'S' (aplikacyjny/separacyjny).
|
||||
"""
|
||||
out: list[dict] = []
|
||||
n = len(positions)
|
||||
for i in range(n):
|
||||
@@ -51,9 +70,16 @@ def find_aspects(
|
||||
for asp, angle in MAJOR.items():
|
||||
dev = abs(sep - angle)
|
||||
if dev <= allowed:
|
||||
out.append({
|
||||
applying = _is_applying(
|
||||
float(la), float(lb), a.get("speed"), b.get("speed"), angle
|
||||
)
|
||||
row = {
|
||||
"obj1": a["name"], "obj2": b["name"],
|
||||
"aspect": asp, "orb": round(dev, 2), "allowed": round(allowed, 2),
|
||||
})
|
||||
}
|
||||
if applying is not None:
|
||||
row["applying"] = applying
|
||||
row["as"] = "A" if applying else "S"
|
||||
out.append(row)
|
||||
break # jedna para = jeden aspekt
|
||||
return out
|
||||
|
||||
Reference in New Issue
Block a user