#include "fiko/zeka/dedektorler.hpp" #include "fiko/zeka/dedektor.hpp" #include "fiko/zeka/sorgu.hpp" #include #include #include #include #include #include #include // ============================================================================ // Pattern + Simetri dedektörleri — SADECE graftan çalışır (mesh/BREP taraması // yok; topoloji katmanı yuzey_tip/alan/merkez/normal/eksen/eksen_nokta/yaricap // özelliklerini zaten yazdı). Kaba ama BREP-veri tabanlı; sıcak yol değil. // ============================================================================ namespace fiko::zeka { namespace { constexpr f64 kPi = 3.14159265358979323846; Vec3d ozVec3(const GrafDeposu& g, u64 dugum, std::string_view anahtar) { const Deger* d = g.oz(dugum, anahtar); const Vec3d* v = d ? std::get_if(d) : nullptr; return v ? *v : Vec3d{}; } const std::string* ozMetin(const GrafDeposu& g, u64 dugum, std::string_view anahtar) { const Deger* d = g.oz(dugum, anahtar); return d ? std::get_if(d) : nullptr; } // Ters yön aynı adımı/düzlemi tanımlar: tolerans üstü ilk bileşeni pozitif yap // (deterministik kanonik işaret). Vec3d isaretKanonik(Vec3d v, f64 tol = 1e-9) { const f64 onder = std::abs(v.x) > tol ? v.x : std::abs(v.y) > tol ? v.y : v.z; return onder < 0 ? -v : v; } int noktaBul(const std::vector& noktalar, Vec3d hedef, f64 tol) { for (size_t i = 0; i < noktalar.size(); ++i) if (noktalar[i].mesafe(hedef) <= tol) return (int)i; return -1; } struct Zincir { std::vector uyeler; Vec3d adim{}; }; struct Daire { bool ok = false; Vec3d eksen{}, merkez{}; f64 aciAdimDeg = 0; }; struct GrupUye { u64 dugum = 0; Vec3d merkez{}, eksen{}; }; // --------------------------------------------------------------------------- class PatternDedektoru final : public Dedektor { public: // Faz 2: AyarSistemi kayıtları (zeka.pattern_*) bu eşiklere bağlanacak. f64 olcuOran = 0.02; // grup ölçü toleransı (yarıçap/alan, oran) f64 konumTol = 0.1; // mm — merkez/adım eşleşmesi f64 aciTolDeg = 1.0; // dairesel eşit-açı + eksen paralellik toleransı const char* ad() const override { return "pattern"; } const char* katman() const override { return "feature"; } void calistir(DedektorBaglam& b) override { const auto yuzler = Sorgu(b.graf).tip("Yuz").govdede(b.govdeVarlik).bul(); // Silindirler öncelik: delik dizileri asıl hedef (temsilci = eksen_nokta). grupla(b, yuzler, "silindir"); grupla(b, yuzler, "duzlem"); } private: void grupla(DedektorBaglam& b, const std::vector& yuzler, std::string_view tip) { const bool silindirMi = tip == "silindir"; struct Olculu { GrupUye uye; f64 olcu = 0; }; std::vector hepsi; for (u64 y : yuzler) { const std::string* t = ozMetin(b.graf, y, "yuzey_tip"); if (!t || *t != tip) continue; Olculu o; o.uye.dugum = y; if (silindirMi) { o.olcu = b.graf.ozSayi(y, "yaricap"); o.uye.merkez = ozVec3(b.graf, y, "eksen_nokta"); o.uye.eksen = ozVec3(b.graf, y, "eksen"); } else { o.olcu = b.graf.ozSayi(y, "alan"); o.uye.merkez = ozVec3(b.graf, y, "merkez"); o.uye.eksen = ozVec3(b.graf, y, "normal"); } hepsi.push_back(o); } std::sort(hepsi.begin(), hepsi.end(), [](const Olculu& a, const Olculu& o2) { return a.olcu != o2.olcu ? a.olcu < o2.olcu : a.uye.dugum < o2.uye.dugum; }); size_t i = 0; while (i < hepsi.size()) { size_t j = i + 1; while (j < hepsi.size() && hepsi[j].olcu <= hepsi[i].olcu * (1.0 + olcuOran) + 1e-9) ++j; if (j - i >= 3) { std::vector grup; for (size_t k = i; k < j; ++k) grup.push_back(hepsi[k].uye); grupIsle(b, std::move(grup)); } i = j; } } void grupIsle(DedektorBaglam& b, std::vector grup) { while (grup.size() >= 3) { std::vector noktalar; for (const GrupUye& u : grup) noktalar.push_back(u.merkez); // Dairesel deneme tüm kalan kümeye bakar (alt-küme kombinatoriği yok — // kaba ama yeterli); başarılıysa küme zaten en büyük tutarlı dizidir. const Daire daire = daireAra(grup, noktalar); if (daire.ok) { std::vector uyeler; for (const GrupUye& u : grup) uyeler.push_back(u.dugum); const u64 p = patternDugum(b, "dairesel", uyeler); b.graf.ozAyarla(p, "adet", (i64)uyeler.size()); b.graf.ozAyarla(p, "aci_adim", daire.aciAdimDeg); b.graf.ozAyarla(p, "eksen", daire.eksen); b.graf.ozAyarla(p, "eksen_nokta", daire.merkez); return; } const Zincir z = enBuyukZincir(noktalar); if (z.uyeler.size() < 3) return; std::vector uyeler; std::vector alinan(grup.size(), false); for (int u : z.uyeler) { uyeler.push_back(grup[u].dugum); alinan[u] = true; } const u64 p = patternDugum(b, "dogrusal", uyeler); b.graf.ozAyarla(p, "adet", (i64)uyeler.size()); b.graf.ozAyarla(p, "adim", z.adim); b.graf.ozAyarla(p, "adim_boy", z.adim.uzunluk()); std::vector kalan; for (size_t k = 0; k < grup.size(); ++k) if (!alinan[k]) kalan.push_back(grup[k]); grup = std::move(kalan); // kalanlarda ikinci dizi aranır } } u64 patternDugum(DedektorBaglam& b, const char* tip, const std::vector& uyeler) const { const u64 p = b.graf.dugumEkle("Pattern"); b.graf.ozAyarla(p, "tip", std::string(tip)); b.graf.iliskiEkle("icerir", b.govdeDugum, p); for (u64 y : uyeler) b.graf.iliskiEkle("uye", p, y); return p; } // En büyük tutarlı doğrusal dizi: adım adayları nokta çiftlerinin farkları; // öncülü olmayan noktadan ileri yürünür (açgözlü alt-küme yerine en uzun zincir). Zincir enBuyukZincir(const std::vector& p) const { Zincir enIyi; std::set> denenen; const auto q = [this](f64 v) { return (i64)std::llround(v / konumTol); }; for (size_t i = 0; i < p.size(); ++i) for (size_t j = 0; j < p.size(); ++j) { if (i == j) continue; const Vec3d adim = isaretKanonik(p[j] - p[i], konumTol); if (adim.uzunluk() <= konumTol) continue; if (!denenen.insert({q(adim.x), q(adim.y), q(adim.z)}).second) continue; for (size_t s = 0; s < p.size(); ++s) { if (noktaBul(p, p[s] - adim, konumTol) >= 0) continue; // öncülü var → baş değil Zincir z; z.adim = adim; int son = (int)s; z.uyeler.push_back(son); for (;;) { const int sonraki = noktaBul(p, p[son] + adim, konumTol); if (sonraki < 0) break; z.uyeler.push_back(sonraki); son = sonraki; } if (dahaIyi(z, enIyi)) enIyi = std::move(z); } } return enIyi; } // Deterministik sıralama: adet ↓, adım boyu ↑, adım bileşenleri ↑. static bool dahaIyi(const Zincir& a, const Zincir& z2) { if (a.uyeler.size() != z2.uyeler.size()) return a.uyeler.size() > z2.uyeler.size(); const f64 la = a.adim.uzunlukKare(), lb = z2.adim.uzunlukKare(); if (la != lb) return la < lb; if (a.adim.x != z2.adim.x) return a.adim.x < z2.adim.x; if (a.adim.y != z2.adim.y) return a.adim.y < z2.adim.y; return a.adim.z < z2.adim.z; } Daire daireAra(const std::vector& grup, const std::vector& noktalar) const { std::vector adaylar; // Grubun ortak yönü (silindir ekseni / düzlem normali) birinci aday — // delik çemberinde eksenler paraleldir, uydurma düzlemini bedavaya verir. const Vec3d ortak = grup[0].eksen.birim(); bool paralel = ortak.uzunlukKare() > 0.5; const f64 esik = std::cos(aciTolDeg * kPi / 180.0); if (paralel) for (const GrupUye& u : grup) { const Vec3d e = u.eksen.birim(); if (e.uzunlukKare() < 0.5 || std::abs(e.nokta(ortak)) < esik) { paralel = false; break; } } if (paralel) adaylar.push_back(ortak); adaylar.push_back({1, 0, 0}); adaylar.push_back({0, 1, 0}); adaylar.push_back({0, 0, 1}); for (const Vec3d& a : adaylar) { const Daire d = daireDene(noktalar, a); if (d.ok) return d; } return {}; } // Merkezler eksene dik tek düzlemde + ortak çemberde + eşit açı aralıklı mı. Daire daireDene(const std::vector& p, Vec3d eksenYon) const { Daire sonuc; const Vec3d n = eksenYon.birim(); if (n.uzunlukKare() < 0.5) return sonuc; Vec3d orta{}; for (const Vec3d& v : p) orta += v; orta = orta / (f64)p.size(); f64 eksenSapma = 0, yayilim = 0; for (const Vec3d& v : p) { eksenSapma = std::max(eksenSapma, std::abs((v - orta).nokta(n))); yayilim = std::max(yayilim, (v - orta).uzunluk()); } if (eksenSapma > konumTol || yayilim <= konumTol) return sonuc; const Vec3d ref = std::abs(n.x) < 0.9 ? Vec3d{1, 0, 0} : Vec3d{0, 1, 0}; const Vec3d u = n.capraz(ref).birim(); const Vec3d v2 = n.capraz(u); const size_t N = p.size(); std::vector px(N), py(N); f64 sxx = 0, sxy = 0, syy = 0, sxb = 0, syb = 0, sb = 0; for (size_t i = 0; i < N; ++i) { const Vec3d f = p[i] - orta; px[i] = f.nokta(u); py[i] = f.nokta(v2); const f64 bb = -(px[i] * px[i] + py[i] * py[i]); sxx += px[i] * px[i]; sxy += px[i] * py[i]; syy += py[i] * py[i]; sxb += px[i] * bb; syb += py[i] * bb; sb += bb; } // Kasa çember uydurması: x²+y²+Dx+Ey+F ≈ 0. Merkezlenmiş koordinatlarda // Σx=Σy=0 → 2x2'ye iner; doğrusal küme determinantı söndürür (çember değil). const f64 det = sxx * syy - sxy * sxy; if (det <= 1e-9 * (sxx + syy) * (sxx + syy)) return sonuc; const f64 D = (sxb * syy - syb * sxy) / det; const f64 E = (syb * sxx - sxb * sxy) / det; const f64 F = sb / (f64)N; const f64 cx = -D / 2, cy = -E / 2; const f64 rKare = cx * cx + cy * cy - F; if (rKare <= konumTol * konumTol) return sonuc; const f64 r = std::sqrt(rKare); if (r > yayilim * 10.0) return sonuc; // dev yarıçap = doğrusala teğet sahte çember std::vector aci(N); for (size_t i = 0; i < N; ++i) { const f64 dx = px[i] - cx, dy = py[i] - cy; if (std::abs(std::sqrt(dx * dx + dy * dy) - r) > konumTol) return sonuc; aci[i] = std::atan2(dy, dx) * 180.0 / kPi; } std::sort(aci.begin(), aci.end()); f64 enAz = 1e18, enCok = -1e18, toplam = 0; for (size_t i = 1; i < N; ++i) { const f64 fark = aci[i] - aci[i - 1]; enAz = std::min(enAz, fark); enCok = std::max(enCok, fark); toplam += fark; } const f64 sarma = aci[0] + 360.0 - aci[N - 1]; f64 aciAdim = 0; if (std::max(enCok, sarma) - std::min(enAz, sarma) <= aciTolDeg) aciAdim = 360.0 / (f64)N; // tam tur else if (enCok - enAz <= aciTolDeg) aciAdim = toplam / (f64)(N - 1); // kısmi yay dizisi else return sonuc; if (aciAdim <= aciTolDeg) return sonuc; sonuc.ok = true; sonuc.eksen = isaretKanonik(n); sonuc.merkez = orta + u * cx + v2 * cy; sonuc.aciAdimDeg = aciAdim; return sonuc; } }; // --------------------------------------------------------------------------- class SimetriDedektoru final : public Dedektor { public: // Faz 2: AyarSistemi kayıtları (zeka.simetri_*) bu eşiklere bağlanacak. f64 alanOran = 0.02; // alan/yarıçap imza toleransı (oran) f64 konumTol = 0.1; // mm — yansıyan merkez eşleşmesi f64 guvenEsik = 0.98; // SimetriDuzlemi yazma eşiği u32 enCokAday = 8; // baskın normal aday tavanı (fasetli modelde patlamasın) const char* ad() const override { return "simetri"; } const char* katman() const override { return "feature"; } void calistir(DedektorBaglam& b) override { struct Imza { u64 dugum = 0; std::string tip; f64 alan = 0, yaricap = 0; bool yaricapli = false; Vec3d merkez{}, normal{}; }; std::vector yuzler; for (u64 y : Sorgu(b.graf).tip("Yuz").govdede(b.govdeVarlik).bul()) { Imza z; z.dugum = y; const std::string* t = ozMetin(b.graf, y, "yuzey_tip"); z.tip = t ? *t : std::string(); z.alan = b.graf.ozSayi(y, "alan"); const Deger* r = b.graf.oz(y, "yaricap"); z.yaricapli = r && !std::holds_alternative(*r); if (z.yaricapli) z.yaricap = b.graf.ozSayi(y, "yaricap"); z.merkez = ozVec3(b.graf, y, "merkez"); z.normal = ozVec3(b.graf, y, "normal"); yuzler.push_back(std::move(z)); } if (yuzler.size() < 2) return; Vec3d nokta{}; f64 toplamAlan = 0; for (const Imza& z : yuzler) { nokta += z.merkez * z.alan; toplamAlan += z.alan; } if (toplamAlan <= 0) return; nokta = nokta / toplamAlan; // Aday düzlemler: ağırlık merkezinden geçen baskın düzlem normalleri + // eksen hizalı 3 ana düzlem. struct Aday { Vec3d n{}; f64 agirlik = 0; }; std::vector adaylar; const auto adayEkle = [&](Vec3d nn, f64 agirlik) { nn = isaretKanonik(nn.birim()); if (nn.uzunlukKare() < 0.5) return; for (Aday& a : adaylar) if (a.n.nokta(nn) > 0.99996) { // ~0.5° içinde aynı yön a.agirlik += agirlik; return; } adaylar.push_back({nn, agirlik}); }; for (const Imza& z : yuzler) if (z.tip == "duzlem") adayEkle(z.normal, z.alan); std::sort(adaylar.begin(), adaylar.end(), [](const Aday& a, const Aday& a2) { if (a.agirlik != a2.agirlik) return a.agirlik > a2.agirlik; if (a.n.x != a2.n.x) return a.n.x < a2.n.x; if (a.n.y != a2.n.y) return a.n.y < a2.n.y; return a.n.z < a2.n.z; }); if (adaylar.size() > (size_t)enCokAday) adaylar.resize(enCokAday); adayEkle({1, 0, 0}, 0); adayEkle({0, 1, 0}, 0); adayEkle({0, 0, 1}, 0); // Her aday için yüz çoklu-küme imzası: yansıyan merkez, aynı (tip, alan[, // yarıçap]) imzalı başka bir yüzün merkeziyle eşleşiyor mu (kendisi dahil — // düzlem üstündeki yüz kendine yansır). O(F²), sıcak yol değil. for (const Aday& a : adaylar) { u32 eslesen = 0; for (const Imza& f : yuzler) { const Vec3d yans = f.merkez - a.n * (2.0 * (f.merkez - nokta).nokta(a.n)); for (const Imza& g2 : yuzler) { if (g2.tip != f.tip) continue; const f64 buyuk = std::max({f.alan, g2.alan, 1e-9}); if (std::abs(f.alan - g2.alan) > alanOran * buyuk) continue; if (f.yaricapli != g2.yaricapli) continue; if (f.yaricapli && std::abs(f.yaricap - g2.yaricap) > alanOran * std::max(f.yaricap, g2.yaricap) + 1e-9) continue; if (g2.merkez.mesafe(yans) <= konumTol) { ++eslesen; break; } } } const f64 guven = (f64)eslesen / (f64)yuzler.size(); if (guven + 1e-9 < guvenEsik) continue; const u64 d = b.graf.dugumEkle("SimetriDuzlemi"); b.graf.ozAyarla(d, "normal", a.n); b.graf.ozAyarla(d, "nokta", nokta); b.graf.ozAyarla(d, "guven", guven); b.graf.iliskiEkle("icerir", b.govdeDugum, d); } } }; } // namespace std::unique_ptr patternDedektoru() { return std::make_unique(); } std::unique_ptr simetriDedektoru() { return std::make_unique(); } } // namespace fiko::zeka