Files
kolaytercih/scripts/il-izgara-uret.ts
bilalgursen c60e3ab409
All checks were successful
Deploy / deploy (push) Successful in 4m34s
fix(tasarim): şehir haritası kartı arka plan gibi kaplar, kenarlar erir; pikseller azaldı
- Izgara 72 → 56 sütun, serpinti kaldırıldı (Bilal: "piksel detaylarını azalt").
- Harita `doldur` (slice) ile SehirYuzeyi kartını kaplar, metin üstünde; iki
  katlı mask ile hiçbir kenarda kesik kare kalmaz (Bilal: "canvas net bir
  şekilde kesiliyor"). Telefonda altta, lg'de sağ yarıda belirir.
- Yakınlaşma kutusu kabın oranında kurulur, il `odakHiza` ile kabın istenen
  noktasına gelir (şehir sayfası: lg sağ orta, telefon alt orta; konum kartı orta).
- usePixelHeat meet/slice + ortalama ofsetini hesaba katar.
- Üniversite konum kartı: kartı doldurur, radyal erime.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 15:10:44 +03:00

171 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Şehir haritası (DESIGN.md §4.17) için il ve ülke kontürlerini hero'nun
// dot-matrix ızgarasına rasterize eder. Hero'daki TurkeyPixelMap 64 sütun;
// burada 56 sütun (Bilal, 24 Eyl 2026: "piksel detaylarını azalt"); her il en az birkaç kare alsın (Yalova, Kilis gibi
// küçük iller için 2×2 blok garantisi vardır). Çıktı
// src/lib/il-izgara-verisi.ts dosyasına yazılır ve commit'lenir.
//
// Kaynak: turkey-map-react il path'leri (viewBox 1050×585, üstten 138 kırpık)
// ve harita-ulkeler-verisi.ts ülke kontürleri (1050×447). Hücre merkezi
// poligonun içindeyse hücre o ile aittir (ray casting; Bézier eğrileri uç
// noktalarıyla düzleştirilir — hücre ölçeğinde fark etmez).
//
// Çalıştırma: pnpm tsx scripts/il-izgara-uret.ts
import fs from "node:fs";
import path from "node:path";
import { cities } from "turkey-map-react/lib/data";
import { normalizeIlAdi, HARITA_UST_KIRPMA, HARITA_GENISLIK, HARITA_YUKSEKLIK } from "../src/lib/harita";
import { ULKE_HARITALARI } from "../src/lib/harita-ulkeler-verisi";
const SUTUN = 56;
const HUCRE = HARITA_GENISLIK / SUTUN;
const YUKSEKLIK = HARITA_YUKSEKLIK - HARITA_UST_KIRPMA; // 447
const SATIR = Math.ceil(YUKSEKLIK / HUCRE);
type Nokta = [number, number];
type Hucre = [number, number];
function poligonlar(d: string): Nokta[][] {
const toks = d.match(/[MLHVCZmlhvcz]|-?\d*\.?\d+(?:e-?\d+)?/g) ?? [];
const polys: Nokta[][] = [];
let cur: Nokta[] = [];
let x = 0;
let y = 0;
let cmd = "";
let i = 0;
const num = () => Number(toks[i++]);
const kapat = () => {
if (cur.length > 2) polys.push(cur);
cur = [];
};
while (i < toks.length) {
const t = toks[i];
if (/[A-Za-z]/.test(t)) {
cmd = t;
i++;
if (cmd === "Z" || cmd === "z") kapat();
continue;
}
switch (cmd) {
case "M": x = num(); y = num(); kapat(); cur = [[x, y]]; cmd = "L"; break;
case "m": x += num(); y += num(); kapat(); cur = [[x, y]]; cmd = "l"; break;
case "L": x = num(); y = num(); cur.push([x, y]); break;
case "l": x += num(); y += num(); cur.push([x, y]); break;
case "H": x = num(); cur.push([x, y]); break;
case "h": x += num(); cur.push([x, y]); break;
case "V": y = num(); cur.push([x, y]); break;
case "v": y += num(); cur.push([x, y]); break;
case "C": num(); num(); num(); num(); x = num(); y = num(); cur.push([x, y]); break;
case "c": num(); num(); num(); num(); x += num(); y += num(); cur.push([x, y]); break;
default: i++;
}
}
kapat();
return polys;
}
function icinde(px: number, py: number, poly: Nokta[]): boolean {
let ic = false;
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
const [xi, yi] = poly[i];
const [xj, yj] = poly[j];
if (yi > py !== yj > py && px < ((xj - xi) * (py - yi)) / (yj - yi) + xi) ic = !ic;
}
return ic;
}
/** Her hücre merkezini ilk eşleşen bölgeye verir; sıra öncelik demektir. */
function rasterize(
bolgeler: { ad: string; polys: Nokta[][] }[],
ustKirpma: number,
): Map<string, Hucre[]> {
const sonuc = new Map<string, Hucre[]>();
for (let r = 0; r < SATIR; r++) {
for (let q = 0; q < SUTUN; q++) {
const px = (q + 0.5) * HUCRE;
const py = ustKirpma + (r + 0.5) * HUCRE;
const b = bolgeler.find((b) => b.polys.some((p) => icinde(px, py, p)));
if (!b) continue;
const liste = sonuc.get(b.ad) ?? [];
liste.push([q, r]);
sonuc.set(b.ad, liste);
}
}
return sonuc;
}
// ---- Türkiye illeri
const iller = cities.map((c) => ({ ad: normalizeIlAdi(c.name), polys: poligonlar(c.path) }));
const ilHucre = rasterize(iller, HARITA_UST_KIRPMA);
// Küçük il garantisi: 4 hücreden azı olan il bbox merkezinde 2×2 blok alır,
// o hücreler komşulardan düşer.
for (const il of iller) {
const mevcut = ilHucre.get(il.ad) ?? [];
if (mevcut.length >= 4) continue;
const pts = il.polys.flat();
const cx = (Math.min(...pts.map((p) => p[0])) + Math.max(...pts.map((p) => p[0]))) / 2;
const cy = (Math.min(...pts.map((p) => p[1])) + Math.max(...pts.map((p) => p[1]))) / 2;
const q0 = Math.min(SUTUN - 2, Math.max(0, Math.round(cx / HUCRE) - 1));
const r0 = Math.min(SATIR - 2, Math.max(0, Math.round((cy - HARITA_UST_KIRPMA) / HUCRE) - 1));
const blok: Hucre[] = [[q0, r0], [q0 + 1, r0], [q0, r0 + 1], [q0 + 1, r0 + 1]];
const anahtar = new Set(blok.map(([q, r]) => `${q},${r}`));
for (const [ad, liste] of ilHucre) {
if (ad === il.ad) continue;
ilHucre.set(ad, liste.filter(([q, r]) => !anahtar.has(`${q},${r}`)));
}
const birlesik = new Map(mevcut.map((h) => [`${h[0]},${h[1]}`, h]));
for (const h of blok) birlesik.set(`${h[0]},${h[1]}`, h);
ilHucre.set(il.ad, [...birlesik.values()]);
}
// ---- Ülkeler (KKTC, yurt dışı): ülke öncelikli, bağlam ikincil
const ulkeHucre: Record<string, { ulke: Hucre[]; baglam: Hucre[] }> = {};
for (const [kod, harita] of Object.entries(ULKE_HARITALARI)) {
const r = rasterize(
[
{ ad: "ulke", polys: harita.ulke.flatMap(poligonlar) },
{ ad: "baglam", polys: harita.baglam.flatMap(poligonlar) },
],
0,
);
ulkeHucre[kod] = { ulke: r.get("ulke") ?? [], baglam: r.get("baglam") ?? [] };
}
const enKucuk = [...ilHucre.entries()].sort((a, b) => a[1].length - b[1].length).slice(0, 5);
console.log(
`${SUTUN}×${SATIR}, hücre ${HUCRE.toFixed(3)}; kara ${[...ilHucre.values()].reduce((a, b) => a + b.length, 0)}; en küçük: ${enKucuk.map(([a, l]) => `${a}:${l.length}`).join(" ")}`,
);
const satirlar = [...ilHucre.entries()]
.sort((a, b) => a[0].localeCompare(b[0], "tr-TR"))
.map(([ad, liste]) => ` ${JSON.stringify(ad)}: ${JSON.stringify(liste)},`);
const ulkeSatirlar = Object.entries(ulkeHucre).map(
([kod, v]) => ` ${JSON.stringify(kod)}: { ulke: ${JSON.stringify(v.ulke)}, baglam: ${JSON.stringify(v.baglam)} },`,
);
const cikti = `// Bu dosya scripts/il-izgara-uret.ts tarafından üretildi — elle düzenleme.
// Hero'nun dot-matrix ızgarasında (${SUTUN} sütun × ${SATIR} satır, hücre ${HUCRE.toFixed(4)}
// viewBox birimi) her ilin ve ülkenin kapladığı hücreler [sütun, satır].
import type { UlkeKodu } from "./harita-ulkeler-verisi";
export const IL_IZGARA = {
sutun: ${SUTUN},
satir: ${SATIR},
hucre: ${HUCRE.toFixed(4)},
} as const;
export type IzgaraHucre = readonly [number, number];
/** Anahtar DB biçiminde il adı (normalizeIlAdi). */
export const IL_HUCRELERI: Record<string, readonly IzgaraHucre[]> = {
${satirlar.join("\n")}
};
export const ULKE_HUCRELERI: Record<UlkeKodu, { ulke: readonly IzgaraHucre[]; baglam: readonly IzgaraHucre[] }> = {
${ulkeSatirlar.join("\n")}
};
`;
const hedef = path.join(process.cwd(), "src/lib/il-izgara-verisi.ts");
fs.writeFileSync(hedef, cikti);
console.log("yazıldı:", hedef, `${(cikti.length / 1024).toFixed(0)} KB`);