# Plan: Uranyen Astroloji — 90 Derece Dial (90° Kadran)

## Kaynak
Kullanıcının sağladığı kaynak: `C:\Users\nolur\Downloads\uranyen_astroloji.pdf` (Sevilay Eriçdem, "Uranyen Astroloji", 419 sayfa). Bu plan, kitaptaki matematiksel/teknik yöntemi (Alfred Witte / Hamburg Okulu'nun kamuya mal olmuş 90° dial tekniği) referans alır; kitabın yorum/anlatım metinleri kopyalanmamıştır.

## Goal
Mevcut doğum haritası sistemine, Uranyen astrolojideki **90 derece dial** görünümünü eklemek:
- Gezegen/nokta boylamlarının 90°'ye indirgenmiş (mod 90) karşılıklarını hesaplamak,
- 8 Transneptünyen (Uranyen hipotetik) noktanın (Cupido, Hades, Zeus, Kronos, Apollon, Admetos, Vulcanus, Poseidon) haritaya dahil edilmesini sağlamak,
- Bu 90°'lik uzayda sert açı (kavuşum/kare/karşıt/yarı kare/sesquikuadrat) örtüşmelerini ve orta nokta (midpoint) ilişkilerini tespit etmek,
- Bunu, kitaptaki gerçek Uranyen yazılımlarına benzer, **tek bir 0-90° yelpaze/kadran** olarak (mevcut 360°'lik tam çark değil) görselleştirmek.

Kullanıcı net olarak "sadece 90'lık harita" istediğini belirtti — Meridyen Ev Sistemi, Solar Arc zamanlama teknikleri, orta nokta ağaçlarının tam yorumlama metni gibi kitaptaki diğer Uranyen teknikleri bu planın kapsamı DIŞINDADIR (bkz. Out of Scope).

## Technical Choices

- **TNP kapsamı: Dahil edilecek** — Kullanıcı onayıyla 8 Uranyen hipotetik noktası (Cupido, Hades, Zeus, Kronos, Apollon, Admetos, Vulcanus, Poseidon) da dial'a dahil edilecek. *Rasyonel:* Bu noktalar zaten swetest tarafından hesaplanıyor (bkz. Current State), sadece adlandırma hatası düzeltilip veri akışına doğru bağlanması yeterli — ek bir ephemeris motoru gerekmiyor.
- **Görsel stil: Otantik tek-yelpaze 90° dial** (kullanıcı onayı) — Mevcut "Harmonic Chart n=4" (360°'lik çarkta derece*4 mod 360) YENİDEN KULLANILMAYACAK çünkü görsel olarak klasik 360° çark gibi görünür, Uranyen astrologların alıştığı 0-90° yelpaze/ibre görünümünü vermez. Yeni bir SVG render modu yazılacak. *Rasyonel:* Kullanıcı özellikle "90'lık harita" istedi; bu, kadranın kendisinin 90°'lik bir yay olarak görünmesini ima eder.
- **Hesaplama motoru:** `SwetestService` (swetest CLI wrapper) — yeni bir ephemeris entegrasyonuna gerek yok, mevcut `-p` flag dizisinde Uranyen noktalar zaten isteniyor (bkz. Current State Q1).
- **Chart tipi kalıcılığı:** DB migration YOK — mevcut pattern'e uyularak (harmonic, sidereal gibi) tamamen runtime/query-param bazlı bir görünüm modu olarak eklenecek.
- **Orb değerleri (kitaptan):** Midpoint/hassas nokta orb'u **1°**, natal harita sert açı orb'u **2°**, transit orb'u **0°30'**. Bunlar mevcut `aspectTypeFromDiff()`'in genel 3-8° orb'larından farklı ve KASITLI olarak daha dar tutulacak — Uranyen sistemde exact temas esastır. Bu yüzden dial için ayrı, parametrik bir açı/orb fonksiyonu yazılacak (mevcut fonksiyon değiştirilmeyecek, yan yana yeni bir fonksiyon eklenecek).

## Current State Analysis

Kod tabanı Laravel + server-side SVG (Blade) render eden, React/Vue kullanmayan bir astroloji backend'i. Chart hesaplama tamamen `SwetestService` (Swiss Ephemeris `swetest` CLI wrapper) üzerinden yapılıyor; chart TİPİ hiçbir yerde DB'de saklanmıyor, tamamen query-param bazlı bir kavram.

### Doğrulanmış Kritik Bulgu: TNP'ler zaten hesaplanıyor
`storage/app/sweph/swetest.exe -hplan` çıktısı ile doğrulandı — swetest'in "fictitious objects" harf kodları:
```
J Cupido   K Hades   L Zeus     M Kronos
N Apollon  O Admetos P Vulkanus Q Poseidon
```
`SwetestService.php:61` ve `:1746`'daki üretim flag dizisi:
```php
$flags = '-p0123456789DAmtGHIFJfJKLMNOPQRSTUVWXYZ';
```
**J,K,L,M,N,O,P,Q harfleri zaten bu dizide mevcut** — yani ana `calculate()` çağrısı zaten Cupido..Poseidon'u swetest'ten istiyor. Gerçek veriyle test edildi (`swetest.exe -p...flags... -b8.1.1935 -ut02:18:53 -house-90.0817,32.2411,P`) ve şu satırlar doğrulandı:
```
Cupido         , 154.2528912, -0.0128506
Hades          , 11.7459435,  0.0044192
Zeus           , 132.7150602, -0.0131702
Kronos         , 41.6863236, -0.0046921
Apollon        , 160.5830338, -0.0066942
Admetos        , 11.0199448,  0.0028682
Vulcanus       , 74.5710955, -0.0096438   ← "Vulcanus" (c ile), "Vulkanus" (k) DEĞİL
Poseidon       , 183.1364830, -0.0014138
```
`SwetestService::parseOutput()` (satır 1133-1239) generic bir parser — `$planets[$name] = [...]` şeklinde swetest'in verdiği ismi doğrudan anahtar olarak kullanıyor. Yani `$data['planets']['Cupido']`, `['Hades']`, ... `['Vulcanus']` (c ile) zaten dolu geliyor.

**Bug:** `PlanetCatalog.php` (satır 85, 169, 222, 505), `AiService.php` (satır 1741, 1751, 1836, 1850, 2711, 2779) ve `IndexController.php` (satır 1988) hep **"Vulkanus"** (k ile) anahtarını arıyor. swetest'in ürettiği **"Vulcanus"** (c ile) ile hiç eşleşmiyor → Vulkanus verisi şu an sessizce kayboluyor / hiçbir tabloda görünmüyor.

Ayrıca flag dizisinde `J` iki kez var (`...IFJfJKLM...`) — Cupido gereksiz yere iki kez hesaplanıyor (zararsız ama gereksiz). Dizi ayrıca Uranyen sistemde kullanılmayan 9 fazladan fictitious body da istiyor (Isis-Transpluto, Nibiru, Harrington, Leverrier/Adams/Lowell/Pickering'in gezegenleri, Vulcan, White Moon — harfler R-Z). Bunlar `$data['planets']` içinde fazladan anahtar olarak duruyor ama `PlanetCatalog`'da tanımlı olmadıkları için downstream filtrelemede zaten görmezden geliniyorlar.

### Key Files:

- `app/Services/SwetestService.php` — Ana hesaplama motoru (swetest CLI wrapper, proc_open).
  - `calculate()` (29-143): Ana natal/transit hesaplama, flags satır 61.
  - `parseOutput()` (1133-1239): swetest çıktısını `$data['planets'][isim] = ['degree','speed','decl','order']` şekline parse eder — generic, isim eşleştirmesi yok.
  - `calculateMidpoint()` (1395-1410): **Zaten var**, shortest-arc midpoint formülü (Uranyen sistemle uyumlu), ama private ve sadece 2 tam harita (composite) karşılaştırması için kullanılıyor. Tek haritanın kendi içindeki N adet noktanın ikili midpoint'lerini (midpoint tree) çıkaran bir fonksiyon YOK.
  - `applyHarmonicChart()` (1943-2013) / `normalizeHarmonicFactor()` (2027-2037): `degree = norm360(degree * factor)` — yeni chart-tipi eklemenin referans PATTERN'i (bkz. Task 2, 3).
  - `applySiderealDChart()` (1867-1933) ve `transformLongitudeByDChart()` (2039+): Vedik divisional chart sistemi — farklı bir ekol (Parasara), 90 dial ile matematiksel ilişkisi yok, referans alınmayacak.
- `app/Services/Chart/PlanetCatalog.php` — Gezegen/nokta anahtar listesi, sembol/renk eşlemesi.
  - `uranianKeys()` (503-506): `['Hades','Zeus','Kronos','Apollon','Admetos','Vulkanus','Poseidon']` — **Cupido eksik**, `asteroidKeys()`'e (251-254) yanlışlıkla dahil edilmiş.
  - `map()` (77-87, 161-171) ve sembol tablosu (210-223): "Vulkanus" yazım hatası burada.
- `app/Services/ChartVisualizerService.php` (5000+ satır) — Ham dereceyi SVG koordinatına, açı tablosuna çevirir.
  - `polar()` (4770-4777): `deg → (x,y)` dönüşümü, **her zaman 360°'lik daireyi varsayar** (`$cx=400,$cy=400,$r_dis=398`, satır 11-30). 90° dial için yeni bir geometri fonksiyonu gerekecek (bkz. Task 4).
  - `aspectTypeFromDiff()` (2799-2837): Kavuşum/kare/karşıt/yarı-kare(45°)/sesquikuadrat(135°) zaten tanımlı ama orb'lar (2-8°) Uranyen dial için fazla geniş ve parametrik değil.
  - `spreadAngles()` (3448): Çakışan gezegenleri açısal olarak ayıran cluster mantığı — 90° dial'da (4 burcun üst üste binmesi nedeniyle) çok daha sık çakışma olacağından bu mantık dial'a uyarlanmalı.
  - `prepareVisualData($data, $options)`: Ana giriş noktası; `$options` içindeki bayraklarla (örn. `is_harmonic_view`) davranış değiştiriyor — dial için `is_uranian_dial_view` benzeri yeni bir bayrak eklenecek.
- `app/Services/Chart/ChartPageBuilderService.php` — Web panel orkestrasyonu.
  - Satır 808-885: `harmonic` chart'ının lazy-load edilme PATTERN'i (request param oku → `applyHarmonicChart()` çağır → sonucu `$harmonicData`'ya koy).
  - Satır 1191-1222: `$charts['harmonic'] = ['title'=>.., 'visual'=>$this->visualizer->prepareVisualData($harmonicData, $harmonicOptions), 'raw'=>$harmonicData]` — dial için birebir aynı pattern izlenecek.
  - Satır 1443, 1527: İzin verilen chart tipi listeleri (combo/lazy-load validation) — yeni tip buraya eklenmeli.
  - Satır 1616, 1646: `$txt['harmonic'] => 'Harmonic'` — dil/etiket sözlüğü, dial için `$txt['uranian_dial']` eklenmeli.
- `app/Http/Controllers/Api/AstroController.php` — Mobil JSON API.
  - `getFullChartData()` (satır 88+): `include=` param validation listesi (satır 78-86) — **`harmonic` bile şu an bu listede yok**, dial eklenirken bu boşluk da (harmonic dahil) değerlendirilmeli.
  - `getChartCombo()` (622-684): `inner`/`outer` validation listesi (628-629) — `harmonic` burada da yok.
- `resources/views/frontend/panel/chart-svg.blade.php` — Server-side SVG şablonu, 360°'lik çarkı Blade döngüleriyle çizer.
- `resources/views/frontend/panel/partials/all-chart-modal.blade.php` (satır 80-85, 501-517) ve `chart-combo-modal.blade.php` (49, 88) — Chart tipi seçim UI'ı, harmonic factor input pattern'i burada.
- `public/frontend/js/panel/panel.js`, `all-chart-modal.js` — Client-side chart tipi seçimi/normalize/validate JS mantığı.
- `database/migrations/2025_12_30_152246_create_user_charts_table.php` — Kişi/doğum verisi şeması; chart tipi alanı YOK (kasıtlı, migration gerekmiyor).

## PDF'ten Çıkarılan Matematiksel Referans (implementasyonda kullanılacak)

**90° dönüşüm formülü:** `dial_derece = fmod(longitude, 90)` (0-90 arası). Kardinal/sabit/değişken burçların aynı derecesi aynı dial noktasına düşer (Koç/Yengeç/Terazi/Oğlak 0° → dial 0°; Boğa/Aslan/Akrep/Kova 0° → dial 0°'a denk 30° blok; İkizler/Başak/Yay/Balık 0° → 60° blok — üç 30°'lik blok üst üste 90° dial'ı oluşturur).

**Sert açılar (aynı dial noktasında görünenler):** Kavuşum(0°), Kare(90°), Karşıt(180°) → dial'da hepsi aynı nokta (mod 90 = 0). Yarı kare(45°), Sesquikuadrat(135°) → dial'da aynı nokta (mod 90 = 45). Ayrıca 16. harmonik "kardinal aks" için 22°30' ve 67°30' noktaları özel olarak işaretlenir (kitap satır 569-571, 1206-1211).

**Kardinal Aks (Koç Ekseni):** 0° Koç noktası = sentetik/sabit bir nokta, ephemeris'ten hesaplanmaz — her zaman 0° Koç/Yengeç/Terazi/Oğlak'ı temsil eder, dial'da her zaman pozisyon 0. Sistemde şu an yok, eklenmesi gerekir (bkz. Task 2).

**Midpoint formülü (kitap satır 1508-1538, kod tabanında zaten var — `calculateMidpoint()`):**
```
diff = deg2 - deg1; normalize to (-180, 180]; direct_midpoint = norm360(deg1 + diff/2)
indirect_midpoint = norm360(direct_midpoint + 180)
```

**Orb kuralları (kitap satır 547-548, 1502-1506):** Midpoint/hassas nokta 1°, Natal sert açı 2°, Transit 0°30', TNP/asteroid-majör gezegen teması max 2°.

**6 Kişisel Nokta (kitap satır 1069-1075, 1878-1885):** Güneş, Ay, MC, ASC, Kuzey Ay Düğümü, Koç Noktası — dial yorumlamasında öncelikli bakılması gereken noktalar; ilk sürümde bunlarla TNP'ler arası kesişimler öncelikli aspect taraması olarak vurgulanabilir (opsiyonel, bkz. Out of Scope).

**Doğrulama test verisi (kitap satır 1137-1220, Elvis Presley örneği):** Doğum: 8 Ocak 1935, 02:18:53 CST(+6:00), 32°N14'28" 090°W04'54". Kitaptaki 90° dial hesabı: Ay Düğümleri (KAD) 1° Kova / (GAD) 121.08° = 1.08° Aslan sabit aks; Eros 18.55° Yay = 78.55° → indirect midpoint 33.55° = 3.55° Aslan; bu noktada Plüton(25° Yengeç 08')/Zeus(12° Aslan 43') midpoint'i (123.55° = 3.55° Aslan) exact eşleşiyor. Bu, Task 6'daki otomatik test için referans fixture olarak kullanılacak.

**Fiziksel/basılı 90° dial aletinin derece skalası (kitap satır 703-705, "90° Dial – Çalışma Kadranı"):** `0-180-120-90-60-45-30-22,5-11,25-5,62` dereceleri gösterilir — yani dış tik halkası tek bir kalınlıkta değil, birden çok seviyede: 30° (burç grubu sınırı), 45° (yarı kare), 22,5°/11,25°/5,625° (16./32./64. harmonik "kadran uyumu" ince tikleri, ardışık yarılama serisi). Ayrıca "Kadran Uyumu" bölümü (satır 595-598) bu ince tikleri `45, 22:30, 11:15, 5:37, 2:48` olarak sıralıyor — Koç noktasının kadran uyumunun bir parçası, faktörler bu açılarda Koç noktasının etkisiyle "yankılanır" deniyor. Bu, dial'ın dış tik halkasının hiyerarşik (kalın/orta/ince) çizilmesi gerektiğinin kaynağı.

**Görsel referans (kullanıcının PDF s.48'den paylaştığı ekran görüntüsü — Elvis Presley örneği, gerçek yazılım çıktısı, kitabın PAINT ile eklediği kırmızı daire/ok işaretlemeleri HARİÇ):** Uzun bir mock-iterasyonu sonucunda doğrulanan katman yapısı:
1. **Tam 360° çember** — 90°'lik bir dilim/yelpaze DEĞİL. Noktalar gerçek zodyak derecelerinde (0-360) duruyor; "90 derecelik" olan şey geometri değil, üstüne binen açı/eşleşme mantığı.
2. **Dış ince tik halkası** — yukarıdaki hiyerarşik derece skalası.
3. **Dış "mikro" etiket bandı** — haritadaki TÜM noktalar (klasik gezegenler, Ay düğümleri, ASC/MC, 8 TNP, asteroidler, sabit yıldızlar) için küçük boy glif+derece+dakika etiketi, sürekli ve dış banda hizalı; 12 burçluk renkli halka VE ev numarası halkası YOK.
4. **İç "makro" gezegen bandı — ayrı bir katman:** Klasik gezegenler VE 8 TNP, dış banttaki küçük etiketlerine ek olarak, İÇERİDE de normal/büyük boy glifle TEKRAR çizilmiş — yani her ikisi de (TNP'ler dahil) dış mikro etiket + iç makro glif olmak üzere ÇİFT gösterime sahip. Asteroid/sabit yıldızlar sadece dış mikro bantta kalıyor gibi görünüyor (iç bantta tekrarlanmıyor).
5. **Açı çizgileri: tüm ikili eşleşmeleri TARAMAZ.** Alt yazılar ("Ay Düğümleri = Eros = Satürn = Admetos/Hades", "Ay Düğümleri = Plüton/Zeus orta noktası") gösteriyor ki çizilen çizgiler, haritadaki OLASI tüm mod-90 çakışmaları değil, kitabın Bölüm 3'te tanımladığı **tek bir anlamlı "gezegen resmi" / orta nokta zincirini** (bir A/B=C veya A+B-C=D formülüyle bulunan zinciri) izole edip gösteriyor. Bu, düz "N nokta arası tüm ikili mod-90 eşleşmelerini bul" yaklaşımından temelde farklı — bkz. Task 2'nin genişletilmiş hâli.
6. **Merkezdeki döndürülebilir ibre + dikey sembol sütunu** — kitapta "hareketli bir kadran/dial kullanılır" diye bahsedilen fiziksel enstrümanın dijital karşılığı; muhtemelen kullanıcı bir dereceye "kilitlenip" o dereceyle temas eden tüm faktörleri okuyor. Tam etkileşim mantığı kitap metninden veya tek bir statik görselden kesin olarak çözülemedi — **VERIFY BEFORE IMPLEMENTING** (bkz. Task 4 notu).

## Tasks

### Task 1: Vulkanus/Vulcanus adlandırma hatasını düzelt (ön koşul bug fix)
Diğer tüm görevlerden ÖNCE yapılmalı çünkü Task 2+ bu veriye güvenecek.
- [ ] `SwetestService::parseOutput()` içine (satır ~1201-1203 civarındaki `mean Apogee → Lilith` normalizasyon bloğunun yanına) `Vulcanus → Vulkanus` normalizasyonu ekle (tek noktadan düzelt, catalog'u swetest'in gerçek çıktısına göre değiştirmek yerine — çünkü "Vulkanus" (Almanca/kitaptaki yazım) zaten UI'da ve AiService'te yaygın kullanılıyor).
- [ ] `PlanetCatalog::asteroidKeys()`'ten `'Cupido'`'yu çıkar, `uranianKeys()`'e ekle (satır 253, 505) — Cupido bir asteroid değil, 8. Uranyen hipotetik nokta.
- [ ] Flag dizisindeki tekrar eden `J`'yi temizle (`SwetestService.php:61,1746`): `-p0123456789DAmtGHIFJfKLMNOPQ` (R-Z'yi de çıkarmayı değerlendir — Isis/Nibiru/Harrington/Leverrier/Adams/Lowell/Pickering/Vulcan/White Moon bu projede kullanılmıyor, gereksiz swetest çıktısı üretiyor).

**Dosyalar:** `app/Services/SwetestService.php`, `app/Services/Chart/PlanetCatalog.php`

### Task 2: Backend — 90° dial dönüşümü, Koç Noktası ve "gezegen resmi" (planetary picture) zinciri hesaplaması
Görsel referans incelemesi sonrası kapsam genişledi: dial'ın açı çizgileri düz bir "tüm ikili mod-90 eşleşmelerini bul" listesi DEĞİL, kitabın Bölüm 3'teki simetri formülleriyle (A/B=C, A+B–C=D) bulunan **tek/az sayıda anlamlı zincir**. Bu yüzden Task 2, sadece bir "midpoint tree" tablosundan, o tabloyu ANLAMLI ZİNCİRLERE ayrıştıran bir adım daha ekleyecek şekilde genişletildi.

- [ ] `SwetestService`'e `applyNinetyDegreeDial(array $data): array` ekle: her `planets[*].degree` için `dial_degree = fmod(degree, 90)` alanı ekler (orijinal `degree`'yi bozmadan, `applyHarmonicChart`'ın "yeni kopya döndür" pattern'ini izleyerek — ama bu kez orijinal 360° dereceyi de sakla, çünkü hem tam boylam hem dial derecesi UI'da gerekecek).
- [ ] Aynı fonksiyon içinde `planets['Aries Point'] = ['degree' => 0, 'dial_degree' => 0, ...]` sentetik noktasını ekle (Koç Noktası — kitap satır 1976-1988).
- [ ] `calculateMidpointTree(array $planets, float $orb = 1.0): array` ekle (yeni, public/protected metod) — tüm ikili kombinasyonları `calculateMidpoint()` (mevcut, 1395-1410) ile hesaplar, her midpoint'in mevcut bir gezegen/noktayla `$orb` içinde exact temas edip etmediğini işaretler. Sonuç: `[['a'=>'Sun','b'=>'Moon','direct'=>.., 'indirect'=>.., 'triggered_by'=>['Mars', ...]], ...]`.
- [ ] **YENİ:** `buildPlanetaryPictures(array $midpointTree, array $personalPoints, float $orb = 1.0): array` ekle — `calculateMidpointTree()`'nin ürettiği ham eşleşme listesini, 6 kişisel noktadan (Güneş/Ay/MC/ASC/Ay Düğümü/Koç Noktası — kitap satır 1069-1075) başlayarak birbirine BAĞLI faktör zincirlerine gruplar (graph traversal: bir kişisel noktayı tetikleyen her midpoint, o midpoint'in bileşenlerini de zincire ekler, zincir yeni tetikleyici bulunamayana kadar büyür). Elvis Presley fixture'ında beklenen çıktı: `Ay Düğümleri → Eros → Satürn → Admetos/Hades` ve `Ay Düğümleri → Plüton/Zeus orta noktası` zincirleri (kitap s.48-49). Bu fonksiyon olmadan dial görselinde "hangi çizgiler çizilecek" sorusu cevapsız kalır.
- [ ] `ChartVisualizerService`'e dial'a özel `dialAspectTypeFromDiff(float $diffDeg, float $orb = 1.0): ?array` ekle — sadece 0°/22.5°/45°/67.5° (dial uzayındaki kavuşum, 16.harmonik kardinal aks, yarı-kare) kontrolü yapar, mevcut geniş-orb'lu `aspectTypeFromDiff()`'e dokunulmaz.

**Dosyalar:** `app/Services/SwetestService.php`, `app/Services/ChartVisualizerService.php`

### Task 3: Web panel ve mobil API'ye "Uranyen 90° Dial" chart tipini bağla
Task 2'deki `applyHarmonicChart` referans pattern'i birebir izlenecek.
- [ ] `ChartPageBuilderService`'e (harmonic bloğunun hemen sonrasına, ~satır 885) `uranianDial` request param kontrolü + `applyNinetyDegreeDial()` çağrısı + `$charts['uranian_dial'] = [...]` ekle.
- [ ] Satır 1443 ve 1527'deki izin verilen chart tipi listelerine `'uranian_dial'` ekle.
- [ ] Satır 1616, 1646'daki `$txt` sözlüğüne `'uranian_dial' => 'Uranyen 90° Dial'` ekle (TR/EN her ikisi varsa).
- [ ] `AstroController::getFullChartData()` — `include=` validation listesine (satır 78-86) `harmonic` (mevcut eksiklik) ile birlikte `uranian_dial`'ı da ekle, natal veriden `applyNinetyDegreeDial()` çağıran bir dal ekle.
- [ ] `AstroController::getChartCombo()` — `inner`/`outer` validation listesine (628-629) `uranian_dial` ekle.

**Dosyalar:** `app/Services/Chart/ChartPageBuilderService.php`, `app/Http/Controllers/Api/AstroController.php`

### Task 4: Otantik 90°'lik dial SVG render modu

**ÖNEMLİ DÜZELTME:** Bu görevin ilk versiyonu, dial'ın 0-90°'lik bir "pasta dilimi/yelpaze" olarak çizilmesi gerektiğini varsayıyordu. Kullanıcının paylaştığı gerçek s.48 görüntüsü incelendiğinde bunun YANLIŞ olduğu ortaya çıktı: gerçek 90° dial görselleri de **tam 360° bir çemberdir**, noktalar gerçek zodyak derecelerinde durur. "90 derecelik" olan şey render geometrisi değil, üstüne binen açı/eşleşme mantığıdır (yukarıdaki "Görsel referans" notuna bakın). Aşağıdaki alt görevler bu düzeltilmiş anlayışa göre yazılmıştır.

**4a — Çift katmanlı nokta render'ı**
- [ ] `ChartVisualizerService`'e yeni bir `prepareDialVisualData(array $data, array $options): array` metodu ekle (mevcut `prepareVisualData`'yı BOZMADAN, paralel bir metod). **Mevcut `polar()` (4770-4777) fonksiyonu ve tam 360° çember geometrisi aynen kullanılır** — yeni bir "dial açısı → koordinat" dönüşümüne gerek YOK, çünkü noktalar gerçek derecelerinde kalıyor.
- [ ] **Dış "mikro" etiket katmanı:** haritadaki TÜM noktalar (klasik gezegenler, düğümler, ASC/MC, 8 TNP, asteroidler, sabit yıldızlar) için küçük glif+derece+dakika etiketi, dış banda hizalı, sürekli.
- [ ] **İç "makro" glif katmanı (YENİ, önceki denemede eksikti):** klasik gezegenler VE 8 TNP için, gerçek derecelerinde, normal/büyük boy glif — asteroid ve sabit yıldızlar bu katmana dahil edilmez (sadece dış mikro bantta kalır). Bu, TNP'lerin klasik gezegenlerle GÖRSEL OLARAK EŞİT ağırlıkta gösterilmesi gerektiği anlamına gelir — mevcut `chart-svg.blade.php`'deki `is_asteroid`/`is_extra`/`extra-hidden` mantığı TNP'leri ikinci sınıf/gizli obje olarak ele alıyor, dial modunda bu davranış override edilmeli.
- [ ] 12 burçluk renkli zodyak halkası ve ev numarası halkası dial modunda YOK (bkz. `zodiacData`/`zodiacSlices`/ev numarası dizilerini boşaltma — mock denemesinde doğrulandı, gerçek koda taşınacak).

**4b — Açı çizgileri: "planetary picture" zinciri, tüm ikili eşleşme DEĞİL**
- [ ] Task 2'deki `buildPlanetaryPictures()` çıktısını kullanarak SADECE o zincir(ler)deki bağlantıları çiz — `calculateAspects()`'teki gibi "N nokta, tüm ikili kombinasyonlar" taraması YAPMA. Bunu yanlış yapmak (tüm 40+ noktanın ikili mod-90 eşleşmelerini çizmek) mock denemelerinde test edildi ve kitaptaki görselin verdiği anlamlı/seçici izlenimi vermediği doğrulandı.

**4c — Doğru dairesel çakışma-önleme algoritması**
- [ ] Mock denemesinde bulunan somut hata: tek yönlü kümülatif "ileri itme" (`$prev + minGap`) algoritması, sık kümelenmiş noktalarda hatayı katlayarak yayıyor ve 360°→0° sarma noktasında hiç düzeltme yapmıyordu — sonuç: noktaların yarısı üst üste yığılıyor, diğer yarısı boş kalıyordu. Doğru bir dairesel etiket-yerleştirme algoritması (ör. çift yönlü/simetrik itme + sarma dahil çoklu geçiş relaksasyonu, ya da mevcut `spreadAngles()` (3448) mantığının bu iki eksiği giderilmiş hâli) yazılmalı ve **gerçek Presley verisiyle görsel olarak kontrol edilmeden** "tamamlandı" denmemeli.

**4d — Merkezdeki döndürülebilir ibre + sembol sütunu**
- [ ] Bu enstrümanın TAM işlevi (hangi dereceye kilitlenip neyi okuduğu) tek bir statik görselden veya kitap metninden kesin çözülemedi. **VERIFY BEFORE IMPLEMENTING** — ya kitabın ilgili bölümü (Kadran Teknikleri, s.21) daha dikkatli okunmalı ya da kullanıcıdan/domain uzmanından ek açıklama istenmeli. v1 için bu enstrümanı basit bir statik özet (o anki en güçlü "planetary picture" zincirinin metinsel/listesel dökümü) olarak yapıp, gerçek döndürülebilir etkileşimi Faz 2'ye bırakmak önerilir.

**4e — Süreç: piksel-sadakat nasıl elde edilir**
"Tıpatıp" bir replikasyon, bu planın kod-okuma ile elde edebileceği bir şey değil — üçüncü parti bir yazılımın (muhtemelen Nova Chart / Witte-Werlag) kendi font/boşluk/yerleşim kurallarını tek bir ekran görüntüsünden kesin olarak reverse-engineer etmenin doğal bir sınırı var. Gerçekçi süreç:
1. Render'ı **mevcut genel `chart-svg.blade.php` motoruna sıkıştırmadan**, izole/bağımsız bir bileşen olarak inşa et (ev halkası, ASC/MC kalın eksen gibi genel motora özgü varsayımlarla savaşmamak için) — mock denemelerinde bu izole yaklaşım çalıştı, motoru zorlamak çalışmadı.
2. Her değişiklikten sonra çıktıyı **gerçek boyutta, gerçek veriyle görsel olarak incele** (küçük thumbnail/yan yana panel karşılaştırması yanıltıcı çıktı — bir önceki denemede bu yüzden bariz bir render hatası fark edilmeden sunulmuştu).
3. Kesin bilinmeyen/tahmini kısımları (merkez enstrüman, tam font) UI'da veya dokümantasyonda "yaklaşık" olarak işaretle, kesinmiş gibi sunma.
4. Mümkünse kullanıcıdan kaynak görüntünün daha yüksek çözünürlüklü/orijinal hâlini iste — sıkıştırılmış ekran görüntüsü üzerinden piksel ölçümü güvenilir değil.

**Dosyalar:** `app/Services/ChartVisualizerService.php`, yeni `resources/views/frontend/panel/dial-svg.blade.php`, `app/Services/Chart/ChartPageBuilderService.php` (veya chart listesini render eden üst view — doğrulanmalı: hangi Blade dosyası `$charts` dizisini dönüp `chart-svg.blade.php`'yi include ediyor)

**Not:** Bu görev planın en büyük ve en belirsiz parçası olmaya devam ediyor — artık render geometrisi netleşti (360° çember) ama çift katmanlı nokta render'ı + zincir-bazlı açı seçimi + doğru çakışma algoritması hâlâ ciddi iş. Ayrı bir alt-plan/spike olarak bölünmesi önerilir.

### Task 5: Frontend UI — "Düzenle" çekmecesi (chartsDrawer) ve "Değiştir" akışı
Kullanıcının istediği gerçek UI akışı doğrulandı: panel sayfasında **"Düzenle" butonu** (`panel.blade.php:960`) `#chartsDrawer` offcanvas'ını açar (tanımı `all-chart-modal.blade.php:90`) — bu, "eklenebilir haritalar" listesinin olduğu yer. Liste Blade'de statik `<li>` döngüsüyle değil, **JS tarafında `LOADABLE_TYPES` objesinden** dinamik üretiliyor (`all-chart-modal.js:270-338`). Bir chart tipi seçildiğinde `refreshChartTypeFromLazyEndpoint()` (`all-chart-modal.blade.php:811+`) `chart_type` parametresiyle lazy-load endpoint'ine (`IndexController::loadChartLazy`, `app/Http/Controllers/Frontend/IndexController.php:1836`) istek atıp haritayı ana görünüme getiriyor.

("Değiştir" butonu — `panel.blade.php:974`, `#btnChartPicker` — sadece 2-3 kişilik ilişki/çoklu haritalarda görünüyor, `chart-combo-modal.blade.php` ile bağlantılı; tekli/ana haritada kullanıcının bahsettiği akış "Düzenle" çekmecesidir.)

- [ ] `public/frontend/js/panel/all-chart-modal.js:318` (harmonic girdisinin hemen altına) `LOADABLE_TYPES` objesine ekle:
  ```js
  uranian_dial: {
      title: loadableChartTitle('uranian_dial', 'Uranyen 90° Dial'),
      icon: 'fa-compass', // veya uygun bir ikon
      house: 'P'
  },
  ```
- [ ] `app/Http/Controllers/Frontend/IndexController.php:1838` — `$allowedChartTypes` string'ine `,uranian_dial` ekle.
- [ ] `all-chart-modal.blade.php:766-783` — JS tarafındaki izinli chart tipi dizisine (`refreshableChartTypes` benzeri) `'uranian_dial'` ekle (harmonic'in listede olduğu yerin yanına).
- [ ] Gerekirse `refreshChartTypeFromLazyEndpoint()` içine (`all-chart-modal.blade.php:836` civarı, harmonic'in `house_system=W` set ettiği yere benzer) `uranian_dial` için özel bir parametre bloğu eklenebilir — v1'de muhtemelen gerekmez (factor'süz, sabit dönüşüm).
- [ ] `chart-combo-modal.blade.php` (satır 49, 88) — iç/dış halka seçim listesine de `uranian_dial` eklenirse (ilişki/çoklu haritalarda dial görünümü istenirse), ama bu OPSİYONEL, kullanıcının asıl istediği akış tekli "Düzenle" çekmecesi.

**Dosyalar:** `public/frontend/js/panel/all-chart-modal.js`, `app/Http/Controllers/Frontend/IndexController.php`, `resources/views/frontend/panel/partials/all-chart-modal.blade.php`, (opsiyonel) `resources/views/frontend/panel/partials/chart-combo-modal.blade.php`

### Task 6: Doğrulama testi (Elvis Presley fixture)
- [ ] `tests/` altına, kitaptaki Elvis Presley örneğini (bkz. yukarıdaki "Doğrulama test verisi") kullanan bir PHPUnit testi ekle: doğum verisini `SwetestService::calculate()` + `applyNinetyDegreeDial()` + `calculateMidpointTree()` üzerinden geçirip, kitaptaki 3.55° Aslan (Plüton/Zeus midpoint = Eros indirect midpoint) sonucunu ±0.1° tolerans ile doğrula.
- [ ] Task 1'deki Vulkanus/Vulcanus düzeltmesi için de bir regresyon testi ekle (swetest çıktısındaki "Vulcanus"un `$data['planets']['Vulkanus']` altında göründüğünü doğrula).

**Dosyalar:** `tests/Feature/` veya `tests/Unit/` altında yeni bir test dosyası (mevcut test dizini yapısına uyularak — doğrulanmalı)

## Success Criteria

### Automated Verification:
- [ ] `php artisan test --filter=NinetyDegreeDial` (Task 6'daki yeni test) geçiyor
- [ ] `php artisan test --filter=Vulkanus` (regresyon testi) geçiyor
- [ ] Mevcut test suite'i kırılmıyor: `php artisan test`

### Manual Verification:
- [ ] Web panelde bir doğum haritası için "Uranyen 90° Dial" seçeneği seçildiğinde, gezegenler tam 360° çember üzerinde gerçek zodyak derecelerinde görünüyor (90°'lik bir dilim DEĞİL)
- [ ] Cupido, Hades, Zeus, Kronos, Apollon, Admetos, Vulkanus, Poseidon hem dış mikro etiket bandında hem iç makro glif katmanında, klasik gezegenlerle eşit görsel ağırlıkta görünüyor
- [ ] 12 burçluk renkli zodyak halkası ve ev numarası halkası dial görünümünde YOK
- [ ] Koç Noktası (0°) dial'da sabit ve doğru şekilde görünüyor
- [ ] Açı çizgileri, tüm ikili mod-90 eşleşmelerini değil, `buildPlanetaryPictures()`'ın bulduğu anlamlı zinciri/zincirleri gösteriyor
- [ ] Elvis Presley referans haritasında beklenen zincir (Ay Düğümleri→Eros→Satürn→Admetos/Hades, Ay Düğümleri→Plüton/Zeus) doğru şekilde tespit ediliyor ve çiziliyor
- [ ] Sık kümelenmiş noktalarda etiketler üst üste binmeden, gerçek boyutta (küçük thumbnail değil) görsel olarak kontrol edildi
- [ ] Mobil API'den `include=uranian_dial` ile istek atıldığında JSON yanıtında `dial_degree` alanları doğru geliyor

## Out of Scope
- Meridyen Ev Sistemi (Uranyen sistemin kendi ev sistemi) — mevcut ev sistemleri (Placidus vb.) korunacak, dial sadece gezegen/nokta konumlarını etkiler.
- Solar Arc, Solar Return, Lunar Return ile Uranyen dial'ın birleştirilmesi (zamanlama teknikleri) — ileride ayrı bir plan.
- Orta nokta ağaçlarının (midpoint tree) tam yorumlama/AI metni üretimi — bu plan sadece matematiksel tespiti kapsar, `AiService`'e Uranyen yorumlama prompt'u eklenmesi ayrı bir iş.
- Simetri prensibinin toplam/farklılık/kendiyle-toplam varyantları (kitap Bölüm 3, "Farklılıklar", "Hassas Noktalar" A+B-C=D formülleri) dışındaki türevleri — Task 2'nin `buildPlanetaryPictures()`'ı temel A/B=C ve A+B-C=D zincirlemesini kapsar, ama "toplamlar" ve "kendiyle toplamlar" (a+a) teknikleri bu fazda YOK.
- 45° ve 30°'lik dial'lar (kitapta da bahsedilen, Martha Lang-Wescott'a ait diğer harmonik dial'lar) — sadece 90°.
- Uranyen font/glyph sembollerinin (Hamburg astro font) düzeltilmesi — `PlanetCatalog.php:216`'daki yorum satırındaki sembol harfleri (S,D,F,G,H,J,K,L) şüpheli/tutarsız görünüyor ama bu ayrı bir görsel-font sorunu, dial matematiğini etkilemiyor.
- Merkezdeki döndürülebilir ibre/cetvel aracının TAM interaktif işlevi (bkz. Task 4d) — v1'de statik bir özet olarak ele alınacak, gerçek etkileşim Faz 2.
- Piksel-birebir ("tıpatıp") görsel replikasyon garantisi — Task 4e'de açıklandığı gibi, üçüncü parti bir yazılımın tam font/spacing kurallarını tek bir ekran görüntüsünden kesin olarak elde etmenin doğal bir sınırı var; hedef "yakın/otantik" bir görünüm, birebir piksel eşleşmesi değil.

## Risks (Pre-Mortem)

### Tigers:
- **Task 4 (otantik 90° SVG render) büyük ölçüde belirsiz kapsam** (HIGH)
  - Mitigation: Task 4'ü ayrı bir alt-plan/spike olarak ele almak, önce statik bir mockup/wireframe ile kullanıcı onayı almak, sonra implementasyona geçmek.
- **Dairesel etiket-yerleştirme (collision avoidance) yanlış yazılırsa fark edilmeden ilerler — bu GERÇEKTEN OLDU.** Plan hazırlığı sırasında yapılan bir mock denemesinde tek yönlü kümülatif itme algoritması (wraparound'suz) kullanıldı; kod hatasız çalıştı ve "makul" bir çıktı üretti sanıldı, ama gerçek boyutta incelendiğinde noktaların yarısının üst üste yığıldığı, diğer yarısının boş kaldığı ortaya çıktı — bu ancak kullanıcı ekran görüntüsü paylaşıp karşılaştırınca fark edildi (HIGH)
  - Mitigation: Task 4c'de belirtildiği gibi, HER görsel değişiklikten sonra çıktı gerçek boyutta/gerçek veriyle incelenmeden "doğru" denmemeli. Küçük thumbnail/yan yana panel karşılaştırması yanıltıcıdır.
- **90° dial'da çakışma (overlap) 360°'ye göre çok daha yoğun olacak** — 4 burç üst üste bindiği için tipik bir haritada 40+ nokta (6 kişisel nokta + 10 klasik gezegen + 8 TNP + asteroidler + sabit yıldızlar) birbirine çok yakın konumlarda toplanabilir (HIGH)
  - Mitigation: Doğru dairesel yayma algoritması (Task 4c) erken prototiplenmeli, gerçek verilerle (Task 6 fixture — 41 noktalık tam set) test edilmeli.
- **Aspect çizgilerini "tüm ikili eşleşme" olarak yorumlamak, kitabın gösterdiğinden temelde farklı bir görsel/anlamsal sonuç verir — bu da GERÇEKTEN YAŞANDI.** İlk mock denemelerinde 41 noktanın tüm ikili mod-90 kombinasyonları taranıp bulunan ~14 eşleşme ayrım gözetmeden çizildi; kullanıcı bunun kitaptaki (tek bir anlamlı zincire odaklanan) görselden temelde farklı olduğunu belirtti (HIGH)
  - Mitigation: Task 2'nin `buildPlanetaryPictures()` fonksiyonu ZORUNLU — düz eşleşme taraması aspect çizgisi kaynağı olarak kullanılmamalı.
- **Vulkanus/Vulcanus düzeltmesi başka sessiz veri kayıplarını ortaya çıkarabilir** — aynı yazım tutarsızlığı deseninin başka noktalarda (örn. farklı transliterasyon farkları) olup olmadığı doğrulanmadı (MEDIUM)
  - Mitigation: Task 1 sonrası `array_diff($returnedSwetestNames, $catalogKeys)` ile tam bir isim eşleşme denetimi scripti çalıştırılmalı.

### Elephants:
- **Kaynak PDF telif hakkına tabi bir kitap** (Sevilay Eriçdem, "Uranyen Astroloji") — implementasyon, Witte/Hamburg Okulu'nun kamuya mal olmuş genel astrolojik TEKNİĞİNİ (90° dial formülü, TNP kavramı — 1920'lerden beri yayında) kod olarak uygulamalı; kitaptaki özgün yorum metinlerini, örnek harita açıklamalarını veya kelimesi kelimesine tanım metinlerini uygulamaya/UI metnine KOPYALAMAMALI.
