#include "akilli_secim.hpp" #include #include #include "fiko/geometri/brep.hpp" #include "fiko/sahne/sahne.hpp" namespace fiko { namespace { f32 g_aciTol = 1.0f; // brep koplanar/paralel/tanjant varsayılanı f32 g_kenarTol = 5.0f; // brep kenarZinciri varsayılanı f32 g_boyutTol = 1e-3f; // brep benzerYuzler/benzerKenarlar varsayılanı bool yuzKomutu(AkilliSecim t) { return t == AkilliSecim::TanjantYuz || t == AkilliSecim::KoplanarYuz || t == AkilliSecim::ParalelYuz || t == AkilliSecim::BenzerYuz || t == AkilliSecim::AyniRenkYuz; } // Efektif (görünen) yüz rengi: kullanıcı override (yuzOz) → STEP kaynak rengi // (brep.yuzRenkleri) → gövde rengi. Render pişirme önceliğiyle aynı sıra. Vec3f efektifYuzRenk(const Dugum& d, u32 ord) { if (const auto it = d.yuzOz.find(ord); it != d.yuzOz.end()) return it->second.renk; const std::vector& yr = d.brep->yuzRenkleri(); if (ord >= 1 && ord <= yr.size()) return yr[ord - 1]; return d.renk; } // Eğri yüzün İÇBÜKEY (delik/cep) mi DIŞBÜKEY (boss/fillet) mi olduğu. // Dönen: -1 içbükey, +1 dışbükey, 0 belirlenemedi. // // NEDEN GEREKLİ (07-17 ÖLÇÜLDÜ): brep::benzerYuzler sözleşmesi "tip+ÖLÇÜ eşleşen tüm // yüzler, konum/oryantasyon BAĞIMSIZ" — yani Ø10 deliği seçince aynı R'deki BOSS ve // FILLET'leri de döner (test: benzer_yuzler_delik_vs_boss_teshis → delik+boss = 2/2). // Motor sözleşmesi doğru ve başka çağıranları var; kullanıcı beklentisi ("bu çaptaki tüm // DELİKLER") burada, UI köprüsünde filtrelenir. // // Yöntem: P = yüzeyde örnek nokta, N = P'de DIŞA bakan gerçek yüzey normali (yüz // orientation'ı dahil), R = P'nin eksene/merkeze göre radyal yönü. N·R>0 → dışbükey. int egriYuzIsaret(const geometri::BrepGovde& b, u32 ord, const geometri::YuzBilgi& y) { const std::vector nok = b.yuzNoktalari(ord); // yerel uzay (üçgenleme düğümleri) int arti = 0, eksi = 0; for (const Vec3d& p : nok) { Vec3d n; if (!b.yuzNoktaNormali(ord, p, n) || n.uzunlukKare() < 1e-12) continue; Vec3d r = p - y.eksenNokta; if (!y.kureMi) r = r - y.eksen * r.nokta(y.eksen); // silindir/koni: eksene dik bileşen if (r.uzunlukKare() < 1e-12) continue; const f64 d = n.birim().nokta(r.birim()); if (d > 0.5) ++arti; else if (d < -0.5) ++eksi; if (arti + eksi >= 8) break; // birkaç örnek yeter (yüz tek karakterli) } if (arti == 0 && eksi == 0) return 0; return arti >= eksi ? 1 : -1; } } // namespace f32 akilliSecimAciTol() { return g_aciTol; } void akilliSecimAciTolAta(f32 v) { g_aciTol = std::clamp(v, 0.0f, 90.0f); } f32 akilliSecimKenarTol() { return g_kenarTol; } void akilliSecimKenarTolAta(f32 v) { g_kenarTol = std::clamp(v, 0.0f, 90.0f); } f32 akilliSecimBoyutTol() { return g_boyutTol; } void akilliSecimBoyutTolAta(f32 v) { g_boyutTol = std::clamp(v, 0.0f, 100.0f); } const char* akilliSecimAd(AkilliSecim tur) { switch (tur) { case AkilliSecim::TanjantYuz: return "teğet yüz"; case AkilliSecim::KoplanarYuz: return "koplanar yüz"; case AkilliSecim::ParalelYuz: return "paralel yüz"; case AkilliSecim::BenzerYuz: return "aynı ölçüde yüz"; case AkilliSecim::KenarZinciri: return "zincir kenarı"; case AkilliSecim::BenzerKenar: return "aynı ölçüde kenar"; case AkilliSecim::AyniRenkYuz: return "aynı renkli yüz"; } return "öğe"; } namespace { // Kaynak öğeden akıllı seçim adaylarını hesaplar (seçime DOKUNMAZ). uygula + peek tek // kaynaktan okusun diye ayrıldı: ikisi farklı kalıba kaysa "çip 6 der, tıkla 4 seçer" // tutarsızlığı olurdu. false = kip uyumsuz / seçim yok / brep yok. bool akilliAdaylar(Sahne& sahne, AkilliSecim tur, SecimOge& kaynak, std::vector& sonuc) { const SecimKip istenen = yuzKomutu(tur) ? SecimKip::Yuz : SecimKip::Kenar; if (sahne.kip() != istenen) return false; if (sahne.secililer().empty()) return false; kaynak = sahne.secililer().back(); // SON seçilen (kullanıcının en son işaret ettiği) Dugum* d = sahne.bul(kaynak.govde); if (!d || !d->brep) return false; const f64 aci = (f64)g_aciTol; const f64 kenarAci = (f64)g_kenarTol; const f64 boyut = (f64)g_boyutTol; sonuc.clear(); switch (tur) { case AkilliSecim::TanjantYuz: sonuc = d->brep->tanjantYuzler(kaynak.alt, aci); break; case AkilliSecim::KoplanarYuz: sonuc = d->brep->koplanarYuzler(kaynak.alt, aci); break; case AkilliSecim::ParalelYuz: sonuc = d->brep->paralelYuzler(kaynak.alt, aci); break; case AkilliSecim::BenzerYuz: { sonuc = d->brep->benzerYuzler(kaynak.alt, boyut); // Delik ⇄ boss/fillet ayrımı: motor yalnız tip+ölçü eşler (ölçüldü). Kaynak EĞRİ // yüzse aynı içbükeylikteki yüzleri bırak → "Ø10 deliği seç" boss/fillet getirmez. geometri::YuzBilgi y0; // alanHesapla=false: egriYuzIsaret yalnız eksen/yarıçap/tip okur. Alan istemek dev // B-spline yüzde BRepGProp'u çağırıp seçim karesini saniyelerce kilitliyordu (ölçüldü). if (d->brep->yuzBilgi(kaynak.alt, y0, false) && !y0.duzlemMi) { const int is0 = egriYuzIsaret(*d->brep, kaynak.alt, y0); if (is0 != 0) { std::vector suzulmus; for (const u32 ord : sonuc) { geometri::YuzBilgi yk; if (!d->brep->yuzBilgi(ord, yk, false)) continue; const int isk = egriYuzIsaret(*d->brep, ord, yk); if (isk == 0 || isk == is0) suzulmus.push_back(ord); // belirsiz → ele } sonuc.swap(suzulmus); } } break; } case AkilliSecim::KenarZinciri: sonuc = d->brep->kenarZinciri(kaynak.alt, kenarAci); break; case AkilliSecim::BenzerKenar: sonuc = d->brep->benzerKenarlar(kaynak.alt, boyut); break; case AkilliSecim::AyniRenkYuz: { // Renk topoloji değil GÖRSEL kimlik: gövdenin tüm yüzlerinde efektif rengi // kaynakla eşit (±1/255 — f32 yuvarlama) olanlar. Kendisi de dahil (seçili // olduğundan uygula/peek zaten saymaz). const Vec3f r0 = efektifYuzRenk(*d, kaynak.alt); const f32 tol = 1.0f / 255.0f; for (u32 ord = 1;; ++ord) { geometri::YuzBilgi yb; if (!d->brep->yuzBilgi(ord, yb, false)) break; // ordinal bitti const Vec3f r = efektifYuzRenk(*d, ord); if (std::abs(r.x - r0.x) <= tol && std::abs(r.y - r0.y) <= tol && std::abs(r.z - r0.z) <= tol) sonuc.push_back(ord); } break; } } return true; } } // namespace int akilliSecimPeek(Sahne& sahne, AkilliSecim tur) { SecimOge kaynak; std::vector sonuc; if (!akilliAdaylar(sahne, tur, kaynak, sonuc)) return 0; // Uygula ile AYNI sayım: zaten seçili olanı sayma (çip "3 daha ekle" der, "6 seç" değil). int yeni = 0; for (const u32 ord : sonuc) if (!sahne.seciliMi({kaynak.govde, ord})) ++yeni; return yeni; } bool akilliSecimOner(Sahne& sahne, AkilliSecim& tur, int& adet) { // Öneri YALNIZ tek öğe seçiliyken (kullanıcı bir şey işaret etti, henüz yaymadı). // Çoklu seçim → kullanıcı zaten toplu iş yapıyor, öneri gürültü olur. if (sahne.secililer().size() != 1) return false; const SecimOge k = sahne.secililer().back(); Dugum* d = sahne.bul(k.govde); if (!d || !d->brep) return false; // En anlamlı türü topolojiden türet (aday sırası = öncelik; ilk >1 kazanır). AkilliSecim adaylar[3]; int n = 0; if (sahne.kip() == SecimKip::Yuz) { geometri::YuzBilgi yb; const bool bilgi = d->brep->yuzBilgi(k.alt, yb, false); // yalnız tip bakılıyor → alan gereksiz if (bilgi && (yb.silindirMi || yb.torusMu || yb.koniMi || yb.kureMi)) { adaylar[n++] = AkilliSecim::BenzerYuz; // "bu çaptaki delikler/yuvarlaklar" adaylar[n++] = AkilliSecim::TanjantYuz; // teğet yüzey grubu (yedek) } else { // düz yüz adaylar[n++] = AkilliSecim::KoplanarYuz; adaylar[n++] = AkilliSecim::ParalelYuz; } } else if (sahne.kip() == SecimKip::Kenar) { adaylar[n++] = AkilliSecim::BenzerKenar; // "bu R'deki filletler" adaylar[n++] = AkilliSecim::KenarZinciri; // teğet zincir (yedek) } else { return false; // gövde/nokta kipinde akıllı yayma yok } for (int i = 0; i < n; ++i) { const int c = akilliSecimPeek(sahne, adaylar[i]); if (c >= 1) { tur = adaylar[i]; adet = c; return true; } } return false; } int akilliSecimUygula(Sahne& sahne, AkilliSecim tur) { SecimOge kaynak; std::vector sonuc; if (!akilliAdaylar(sahne, tur, kaynak, sonuc)) return 0; // Sonucu seçime EKLE (coklu=true → mevcut seçim korunur). Zaten seçili olanı sayma. int eklenen = 0; for (const u32 ord : sonuc) { const SecimOge oge{kaynak.govde, ord}; if (sahne.seciliMi(oge)) continue; sahne.sec(oge, true); ++eklenen; } return eklenen; } } // namespace fiko