#include "fiko/sketch/eskiz_veri.hpp" #include #include namespace fiko::eskiz { int EskizVeri::paramEkle(Skalar deger) { const int ix = (int)p_.size(); p_.push_back(deger); return ix; } u32 EskizVeri::noktaEkle(Skalar x, Skalar y) { Geometri g; g.tip = GeoTip::Nokta; g.a = Nokta{paramEkle(x), paramEkle(y)}; g.kimlik = sonrakiGeoKimlik_++; geo_.push_back(g); return g.kimlik; } u32 EskizVeri::cizgiEkle(Skalar x1, Skalar y1, Skalar x2, Skalar y2) { Geometri g; g.tip = GeoTip::Cizgi; g.a = Nokta{paramEkle(x1), paramEkle(y1)}; g.b = Nokta{paramEkle(x2), paramEkle(y2)}; g.kimlik = sonrakiGeoKimlik_++; geo_.push_back(g); return g.kimlik; } u32 EskizVeri::splineEkle(const std::vector>& noktalar) { if (noktalar.size() < 2) return 0; // spline en az 2 gecis noktasi ister Geometri g; g.tip = GeoTip::Spline; g.a = Nokta{paramEkle(noktalar.front().first), paramEkle(noktalar.front().second)}; g.ekNoktalar.reserve(noktalar.size() - 2); for (std::size_t i = 1; i + 1 < noktalar.size(); ++i) g.ekNoktalar.push_back(Nokta{paramEkle(noktalar[i].first), paramEkle(noktalar[i].second)}); g.b = Nokta{paramEkle(noktalar.back().first), paramEkle(noktalar.back().second)}; g.kimlik = sonrakiGeoKimlik_++; geo_.push_back(g); return g.kimlik; } u32 EskizVeri::daireEkle(Skalar cx, Skalar cy, Skalar r) { Geometri g; g.tip = GeoTip::Daire; g.merkez = Nokta{paramEkle(cx), paramEkle(cy)}; g.ir = paramEkle(r); g.kimlik = sonrakiGeoKimlik_++; geo_.push_back(g); return g.kimlik; } u32 EskizVeri::yayEkle(Skalar cx, Skalar cy, Skalar r, Skalar baslangicAci, Skalar bitisAci) { Geometri g; g.tip = GeoTip::Yay; g.merkez = Nokta{paramEkle(cx), paramEkle(cy)}; g.ir = paramEkle(r); g.ib1 = paramEkle(baslangicAci); g.ib2 = paramEkle(bitisAci); // Uc noktalar da bagimsiz parametre olarak tutulur (cakisik kisitlar // baglayabilsin). Baslangic konumu acilardan turetilir. g.a = Nokta{paramEkle(cx + r * std::cos(baslangicAci)), paramEkle(cy + r * std::sin(baslangicAci))}; g.b = Nokta{paramEkle(cx + r * std::cos(bitisAci)), paramEkle(cy + r * std::sin(bitisAci))}; g.kimlik = sonrakiGeoKimlik_++; geo_.push_back(g); return g.kimlik; } u32 EskizVeri::elipsEkle(Skalar cx, Skalar cy, Skalar a, Skalar b, Skalar aci) { Geometri g; g.tip = GeoTip::Elips; g.merkez = Nokta{paramEkle(cx), paramEkle(cy)}; g.ir = paramEkle(a); // buyuk yaricap g.ir2 = paramEkle(b); // kucuk yaricap g.iaci = paramEkle(aci); // donme g.kimlik = sonrakiGeoKimlik_++; geo_.push_back(g); return g.kimlik; } u32 EskizVeri::eliptikYayEkle(Skalar cx, Skalar cy, Skalar a, Skalar b, Skalar aci, Skalar baslangicAci, Skalar bitisAci) { Geometri g; g.tip = GeoTip::Elips; g.merkez = Nokta{paramEkle(cx), paramEkle(cy)}; g.ir = paramEkle(a); g.ir2 = paramEkle(b); g.iaci = paramEkle(aci); g.ib1 = paramEkle(baslangicAci); g.ib2 = paramEkle(bitisAci); // Uc noktalar bagimsiz param olarak tutulur (cakisik kisitlar baglasin). // Yerel elips noktasi: (a cosθ, b sinθ), sonra aci ile donder + merkez. const Skalar ca = std::cos(aci), sa = std::sin(aci); const Skalar lx1 = a * std::cos(baslangicAci), ly1 = b * std::sin(baslangicAci); const Skalar lx2 = a * std::cos(bitisAci), ly2 = b * std::sin(bitisAci); g.a = Nokta{paramEkle(cx + ca * lx1 - sa * ly1), paramEkle(cy + sa * lx1 + ca * ly1)}; g.b = Nokta{paramEkle(cx + ca * lx2 - sa * ly2), paramEkle(cy + sa * lx2 + ca * ly2)}; g.kimlik = sonrakiGeoKimlik_++; geo_.push_back(g); return g.kimlik; } u32 EskizVeri::kisitEkle(KisitTip tip, std::vector geo, Skalar deger) { Kisit k; k.tip = tip; k.geo = std::move(geo); k.deger = deger; k.kimlik = sonrakiKisitKimlik_++; kisit_.push_back(std::move(k)); return kisit_.back().kimlik; } u32 EskizVeri::kisitEkle(KisitTip tip, std::vector geo, std::vector nokta, Skalar deger) { Kisit k; k.tip = tip; k.geo = std::move(geo); k.nokta = std::move(nokta); k.deger = deger; k.kimlik = sonrakiKisitKimlik_++; kisit_.push_back(std::move(k)); return kisit_.back().kimlik; } void EskizVeri::geoSil(u32 kimlik) { // Once bu geometriye bagli kisitlari kaldir. for (std::size_t i = kisit_.size(); i-- > 0;) { for (u32 gk : kisit_[i].geo) { if (gk == kimlik) { kisit_.erase(kisit_.begin() + (std::ptrdiff_t)i); break; } } } // Geometriyi kaldir. Parametreler p_ icinde birakilir (indeks kaymasi // digerlerini bozmasin diye p_ kompakt edilmez — bos yuva kabul). for (std::size_t i = 0; i < geo_.size(); ++i) { if (geo_[i].kimlik == kimlik) { geo_.erase(geo_.begin() + (std::ptrdiff_t)i); return; } } } void EskizVeri::kisitBoluntuRemap(u32 orijinal, u32 yeni) { // Orijinalin b ucu (nokta=1) yeni parcaya gecti → o uca referansli kisitlari // yeni'ye tasi. Uc belirtmeyen kisitta varsayilan indeks 0 (a) sayilir; b // referansi ancak nokta[] acikca 1 iken vardir, o yuzden nokta[] yoksa gecme. for (auto& k : kisit_) { for (std::size_t i = 0; i < k.geo.size(); ++i) { if (k.geo[i] != orijinal) continue; const int h = (i < k.nokta.size()) ? k.nokta[i] : 0; if (h == 1) k.geo[i] = yeni; // b ucu → yeni parca (b'si de nokta=1) } } } void EskizVeri::kisitUcTemizle(u32 geo, int hangi) { // Sadece bu uca ACIKCA nokta[] ile referans veren kisitlari sil (uc-hedefli). // nokta[] bos olan kisit tum-geo (boyut/yon) demektir → korunur. for (std::size_t i = kisit_.size(); i-- > 0;) { const Kisit& k = kisit_[i]; bool ucHedef = false; for (std::size_t j = 0; j < k.geo.size(); ++j) { if (k.geo[j] != geo) continue; if (j < k.nokta.size() && k.nokta[j] == hangi) { ucHedef = true; break; } } if (ucHedef) kisit_.erase(kisit_.begin() + (std::ptrdiff_t)i); } } void EskizVeri::kisitSil(u32 kimlik) { for (std::size_t i = 0; i < kisit_.size(); ++i) { if (kisit_[i].kimlik == kimlik) { kisit_.erase(kisit_.begin() + (std::ptrdiff_t)i); return; } } } Solver::Sonuc EskizVeri::coz() { sonDurum_ = solver_.coz(p_, geo_, kisit_); return sonDurum_; } Solver::Sonuc EskizVeri::surukle(u32 noktaKimlik, Skalar hx, Skalar hy) { sonDurum_ = solver_.surukle(p_, geo_, kisit_, noktaKimlik, hx, hy); return sonDurum_; } const Geometri* EskizVeri::geoBul(u32 kimlik) const { for (const auto& g : geo_) if (g.kimlik == kimlik) return &g; return nullptr; } EskizVeri::AnalizSonuc EskizVeri::analiz() const { AnalizSonuc r; r.dof = sonDurum_.dof; r.durum = sonDurum_.durum; // Çizgi/Yay uçlarını topla (Çember/Elips tek başına kapalı; Nokta izole aday). struct Uc { Skalar x, y; }; std::vector uclar; for (const Geometri& g : geo_) { if (g.yapi) continue; if (g.tip == GeoTip::Cizgi || g.tip == GeoTip::Yay || g.tip == GeoTip::Spline) { // Spline uclari a/b'de AYNALI (Tur 12 tasarimi) → ayni uc semasi. uclar.push_back({noktaX(g.kimlik, 0), noktaY(g.kimlik, 0)}); uclar.push_back({noktaX(g.kimlik, 1), noktaY(g.kimlik, 1)}); } else if (g.tip == GeoTip::Nokta) { r.izoleNokta++; // serbest nokta (profil-dışı) } else if (g.tip == GeoTip::Daire || g.tip == GeoTip::Elips) { r.kapaliProfil++; // tek başına kapalı kontur } } // Uçları yakınlıkla düğümlere birleştir (UNION-FIND: GEÇİŞLİ). Eski greedy ilk-eşleşme // geçişli değildi → bir kavşakta 3+ uç kümelenince (trim T-kavşağı vb.) düğümü ikiye // bölüp SAHTE derece-1 üretiyordu; Pad aynı noktaları zincirleyip kapatırken analiz // "profil açık" diyordu. Union-find A~B~C'yi tek düğümde toplar → tutarlı. constexpr Skalar tol = 1e-3; const std::size_t n = uclar.size(); std::vector ata(n); for (std::size_t i = 0; i < n; ++i) ata[i] = (int)i; const auto kok = [&](int x) { while (ata[x] != x) { ata[x] = ata[ata[x]]; x = ata[x]; } return x; }; for (std::size_t i = 0; i < n; ++i) for (std::size_t j = i + 1; j < n; ++j) if (std::hypot(uclar[i].x - uclar[j].x, uclar[i].y - uclar[j].y) < tol) ata[kok((int)j)] = kok((int)i); // Kök başına derece = ona değen uç sayısı; derece 1 → dangling (açık uç). std::vector derece(n, 0); for (std::size_t i = 0; i < n; ++i) derece[(std::size_t)kok((int)i)]++; int acikUc = 0; for (std::size_t i = 0; i < n; ++i) if (kok((int)i) == (int)i && derece[i] == 1) { // i kendi kökü (temsilci) + derece 1 r.acikUclar.push_back({uclar[i].x, uclar[i].y}); ++acikUc; } // Kaba profil sayımı: kenar varsa açık uç yoksa 1 kapalı zincir, varsa 1 açık zincir. if (!uclar.empty()) { if (acikUc == 0) r.kapaliProfil++; else r.acikProfil = 1; } return r; } void EskizVeri::yapiAyarla(u32 geoKimlik, bool yapi) { for (auto& g : geo_) if (g.kimlik == geoKimlik) { g.yapi = yapi; return; } } const Nokta* EskizVeri::noktaSec(const Geometri& g, int hangi) const { switch (hangi) { case 0: return g.a.gecerli() ? &g.a : (g.merkez.gecerli() ? &g.merkez : nullptr); case 1: return g.b.gecerli() ? &g.b : nullptr; case 2: return g.merkez.gecerli() ? &g.merkez : nullptr; default: return nullptr; } } bool EskizVeri::geoTamKisitliMi(u32 geoKimlik) const { const Geometri* g = geoBul(geoKimlik); if (!g) return false; const auto& sp = sonDurum_.serbestParamlar; const auto serbestMi = [&](i32 ix) { return ix >= 0 && std::find(sp.begin(), sp.end(), (int)ix) != sp.end(); }; const auto noktaSerbest = [&](const Nokta& n) { return n.gecerli() && (serbestMi(n.ix) || serbestMi(n.iy)); }; if (noktaSerbest(g->a) || noktaSerbest(g->b) || noktaSerbest(g->merkez)) return false; if (serbestMi(g->ir) || serbestMi(g->ir2) || serbestMi(g->iaci) || serbestMi(g->ib1) || serbestMi(g->ib2)) return false; for (const Nokta& n : g->ekNoktalar) if (noktaSerbest(n)) return false; return true; } Skalar EskizVeri::noktaX(u32 geoKimlik, int hangi) const { const Geometri* g = geoBul(geoKimlik); if (!g) return 0.0; const Nokta* n = noktaSec(*g, hangi); return (n && n->gecerli()) ? p_[(std::size_t)n->ix] : 0.0; } Skalar EskizVeri::noktaY(u32 geoKimlik, int hangi) const { const Geometri* g = geoBul(geoKimlik); if (!g) return 0.0; const Nokta* n = noktaSec(*g, hangi); return (n && n->gecerli()) ? p_[(std::size_t)n->iy] : 0.0; } // ---- Serilestirme (SKCH chunk govdesi) ---- namespace { template void yazPod(std::string& o, const T& v) { o.append(reinterpret_cast(&v), sizeof(T)); } } // namespace void EskizVeri::disaAktar(std::string& o) const { // SURUM 2: geometri kaydinin SONUNA ekNoktalar (spline ic gecis noktalari) // eklendi. Okuyucu 1'i de kabul eder (spline'siz eski dosyalar birebir). constexpr u32 SURUM = 3; yazPod(o, SURUM); yazPod(o, (u32)p_.size()); for (Skalar s : p_) yazPod(o, (f64)s); yazPod(o, (u32)geo_.size()); for (const Geometri& g : geo_) { yazPod(o, (u8)g.tip); yazPod(o, (u8)(g.yapi ? 1 : 0)); yazPod(o, (i32)g.a.ix); yazPod(o, (i32)g.a.iy); yazPod(o, (i32)g.b.ix); yazPod(o, (i32)g.b.iy); yazPod(o, (i32)g.merkez.ix); yazPod(o, (i32)g.merkez.iy); yazPod(o, (i32)g.ir); yazPod(o, (i32)g.ir2); yazPod(o, (i32)g.iaci); yazPod(o, (i32)g.ib1); yazPod(o, (i32)g.ib2); yazPod(o, (u32)g.kimlik); yazPod(o, (u32)g.ekNoktalar.size()); for (const Nokta& n : g.ekNoktalar) { yazPod(o, (i32)n.ix); yazPod(o, (i32)n.iy); } // NURBS: knot + weight arrays (SURUM 3+) yazPod(o, (u32)g.knot.size()); for (f64 k : g.knot) yazPod(o, (f64)k); yazPod(o, (u32)g.weight.size()); for (f64 w : g.weight) yazPod(o, (f64)w); } yazPod(o, (u32)kisit_.size()); for (const Kisit& k : kisit_) { yazPod(o, (u8)k.tip); yazPod(o, (u32)k.geo.size()); for (u32 gg : k.geo) yazPod(o, (u32)gg); yazPod(o, (u32)k.nokta.size()); for (int nn : k.nokta) yazPod(o, (i32)nn); yazPod(o, (f64)k.deger); yazPod(o, (u8)(k.suru ? 1 : 0)); yazPod(o, (u32)k.kimlik); yazPod(o, (u32)k.param); } yazPod(o, (u32)sonrakiGeoKimlik_); yazPod(o, (u32)sonrakiKisitKimlik_); } bool EskizVeri::iceAktar(const char* veri, std::size_t n) { const char* p = veri; const char* son = veri + n; const auto yeter = [&](std::size_t k) { return (std::size_t)(son - p) >= k; }; const auto u32r = [&](u32& v) -> bool { if (!yeter(4)) return false; std::memcpy(&v, p, 4); p += 4; return true; }; const auto i32r = [&](int& v) -> bool { if (!yeter(4)) return false; i32 t; std::memcpy(&t, p, 4); p += 4; v = (int)t; return true; }; const auto f64r = [&](f64& v) -> bool { if (!yeter(8)) return false; std::memcpy(&v, p, 8); p += 8; return true; }; const auto u8r = [&](u8& v) -> bool { if (!yeter(1)) return false; std::memcpy(&v, p, 1); p += 1; return true; }; u32 surum = 0; if (!u32r(surum) || surum < 1 || surum > 3) return false; // 1 = spline oncesi (uyumlu) u32 pN = 0; if (!u32r(pN) || pN > 5000000) return false; std::vector np; np.reserve(pN); for (u32 i = 0; i < pN; ++i) { f64 d; if (!f64r(d)) return false; np.push_back(d); } u32 gN = 0; if (!u32r(gN) || gN > 5000000) return false; std::vector ng; ng.reserve(gN); for (u32 i = 0; i < gN; ++i) { Geometri g; u8 tip = 0, yapi = 0; if (!u8r(tip) || !u8r(yapi)) return false; g.tip = (GeoTip)tip; g.yapi = yapi != 0; if (!i32r(g.a.ix) || !i32r(g.a.iy) || !i32r(g.b.ix) || !i32r(g.b.iy) || !i32r(g.merkez.ix) || !i32r(g.merkez.iy) || !i32r(g.ir) || !i32r(g.ir2) || !i32r(g.iaci) || !i32r(g.ib1) || !i32r(g.ib2)) return false; if (!u32r(g.kimlik)) return false; if (surum >= 2) { // SURUM 2: spline ic gecis noktalari (ekNoktalar) u32 ekN = 0; if (!u32r(ekN) || ekN > 100000) return false; g.ekNoktalar.reserve(ekN); for (u32 j = 0; j < ekN; ++j) { Nokta n2; if (!i32r(n2.ix) || !i32r(n2.iy)) return false; g.ekNoktalar.push_back(n2); } } // SURUM 3+: knot + weight (NURBS) if (surum >= 3) { u32 kn = 0, wn = 0; if (!u32r(kn)) return false; g.knot.resize(kn); for (u32 j = 0; j < kn; ++j) { f64 v; if (!f64r(v)) return false; g.knot[(size_t)j] = v; } if (!u32r(wn)) return false; g.weight.resize(wn); for (u32 j = 0; j < wn; ++j) { f64 v; if (!f64r(v)) return false; g.weight[(size_t)j] = v; } } ng.push_back(g); } u32 kN = 0; if (!u32r(kN) || kN > 5000000) return false; std::vector nk; nk.reserve(kN); for (u32 i = 0; i < kN; ++i) { Kisit k; u8 tip = 0; if (!u8r(tip)) return false; k.tip = (KisitTip)tip; u32 gc = 0; if (!u32r(gc) || gc > 100000) return false; k.geo.reserve(gc); for (u32 j = 0; j < gc; ++j) { u32 gg; if (!u32r(gg)) return false; k.geo.push_back(gg); } u32 nc = 0; if (!u32r(nc) || nc > 100000) return false; k.nokta.reserve(nc); for (u32 j = 0; j < nc; ++j) { int nn; if (!i32r(nn)) return false; k.nokta.push_back(nn); } if (!f64r(k.deger)) return false; u8 su = 0; if (!u8r(su)) return false; k.suru = su != 0; if (!u32r(k.kimlik) || !u32r(k.param)) return false; nk.push_back(std::move(k)); } u32 sg = 0, sk = 0; if (!u32r(sg) || !u32r(sk)) return false; p_ = std::move(np); geo_ = std::move(ng); kisit_ = std::move(nk); sonrakiGeoKimlik_ = sg; sonrakiKisitKimlik_ = sk; coz(); // durum/dof tazelenir; baslangic tahmini p_ → iyi-kisitli eskizde konum sabit return true; } } // namespace fiko::eskiz