#pragma once // Doğrudan Modelleme — Kenar Fillet/Pah canlı gizmosu (push_pull.hpp ikizi). Seçili // kenarda orta noktada tutamaç belirir; tutamaç yakınında tıkla-sürükle → kenardan // uzaklaştıkça yarıçap artar (ucuz görsel önizleme, OCCT yok). Bırakınca tek OCCT // fillet/pah uygulanır (sahne.pahYuvarlatUygula) + undo (DuzenleIslem). #include #include #include #include #include #include #include #include #include "fiko/cekirdek/cekirdek.hpp" #include "dm_tutamac_olay.hpp" #include "fiko/parca/part_design_sistemi.hpp" // parametrik Fillet/Chamfer feature #include "fiko/render/orbit_kamera.hpp" #include "fiko/sahne/sahne.hpp" #include "fiko/sahne/sahne_islem.hpp" #include "fiko/ui/bilesen_ek.hpp" #include "dogrudan_ayar.hpp" #include "gizmo_gorsel.hpp" #include "imgui.h" namespace fiko { struct KenarFilletDurum { bool aktif = false; // mod açık mı (workbench'e bağlı) bool surukluyor = false; // sürükleme sürüyor // İmleç R/C tutamacına YAKIN mı — tuş durumundan BAĞIMSIZ. `ustunde` out-param'ı // (pusulaUstunde'yi besler) bilerek "yakın && sol tuş basılı" demek: yalnız hover'da // fare navigasyonunu bastırmamalı. Dokunmatik yolunun ise tuş BASILMADAN önce // "parmak tutamacın üstünde mi" bilmesi gerek (sürükleme başlayınca nav yerine // sentetik sol-tuş sürer) → ayrı bayrak. Kare-başı yazılır. bool tutamacYakin = false; bool yuvarlat = true; // true=fillet(yarıçap), false=chamfer(mesafe) — radyal menü değiştirir u32 govde = 0, kenar = 0; // hedef Vec3f ortaD{}; // kenar orta (dünya, sürükleme başı) ImVec2 ortaEkran{}; // kenar orta ekran (sürükleme başı) f32 basMesafe = 0; // tıklama anında imlecin ortaya ekran uzaklığı (px) — R bundan // İTİBAREN büyür; tutamaç ortadan 15px yanda olduğundan bu ofset // düşülmezse R tıklar tıklamaz sıçrıyordu (küçük R verilemiyordu) f32 yaricap = 0; // canlı yarıçap (mm) // 3B canlı önizleme (komşu iki düz yüz): kenar boyunca teğet hatları + fillet yayı / // pah kesimi. Eğri komşuda komsuVar=false → ekran-dairesi fallback. Vec3f basD{}, sonD{}; // kenar uçları (dünya) Vec3f n1D{}, n2D{}; // komşu yüz normalleri (dünya, düz yüz) Vec3f a1D{}, a2D{}; // her yüzün İÇİNE (centroid'e) kenar-dik yön — konveks/konkav robust bool konkav = false; // iç köşe mi (fillet yayı ters tarafa bükülür — önizleme düzeltmesi) bool komsuVar = false; char tipBuf[24] = ""; // sürüklerken yazılan sayı → hassas R/mesafe (drag override) DugumPusula eski; // undo snapshot (sürükleme başı) // Async apply hayaletleri — İŞLEM-ID'Lİ: her gövde-grubu işinin KENDİ kaydı var; bitti // callback'i yalnız kendi kaydını siler → biri inince diğerlerinin animasyonu sürer. // Alanlar bırakış ANINDA kopyalanır (yeni seçim/drag bozamaz). struct Bekleyen { u32 id = 0; u32 fk = 0; // feature kimliği (× iptal → featureIptal; async sonuç güvenle atılır) bool yuvarlat = true; f32 deger = 0; Vec3f orta{}; // halka+etiket konumu (grubun ilk kenarı) std::vector> kenarlar; // işlenen kenarlar (dünya uçları) }; std::vector bekleyenler; u32 bekleyenSayac = 0; // iş kimliği üreteci }; // push_pull ile aynı imza kalıbı: ustunde → main pusulaUstunde'ye OR'lanır (kamera/seçim bastır). inline void kenarFilletCizVeIsle(KenarFilletDurum& kf, Sahne& sahne, Cekirdek& cekirdek, parca::PartDesignSistemi& partD, OrbitKamera& kamera, ImDrawList* dl, ImGuiIO& io, bool vpHover, f32 mx, f32 my, f32 vw, f32 vh, f32 enBoy, bool& ustunde) { (void)cekirdek; // artık feature yolu (partD) kullanılıyor; imza push_pull ile paralel kalsın ustunde = false; if (!kf.aktif) return; const Mat4f vp = kamera.izdusum(enBoy) * kamera.gorunum(); const auto proj = [&](const Vec3f& p, ImVec2& s) -> bool { const Vec4f h = vp * Vec4f{p, 1.0f}; if (h.w <= 1e-4f) return false; s = ImVec2(mx + (h.x / h.w * 0.5f + 0.5f) * vw, my + (h.y / h.w * 0.5f + 0.5f) * vh); return true; }; const auto dunyaNok = [&](const Dugum* d, const Vec3d& p) -> Vec3f { const Vec3d w = d->donusum.noktaUygula(p); return {(f32)w.x, (f32)w.y, (f32)w.z}; }; const auto dunyaYon = [&](const Dugum* d, const Vec3d& v) -> Vec3f { const Vec3d w = d->donusum.yonelim.dondur(v); return Vec3f{(f32)w.x, (f32)w.y, (f32)w.z}.birim(); }; // ANİMASYONLU bekleme hayaletleri (iş başına): kenarlar nabız gibi yanıp söner + ortadan // büyüyüp sönen halka — her işin sonucu İNİNCE yalnız o kayıt temizlenir, diğerleri sürer. if (!kf.bekleyenler.empty()) { const f32 t = (f32)ImGui::GetTime(); const f32 f01 = std::fmod(t, 1.2f) / 1.2f; // 0→1 süpürme (tekrarlı) const f32 nabiz = 0.55f + 0.35f * std::sin(t * 4.0f); // yanıp sönme u32 iptalId = 0, iptalFk = 0; // bu kare × ile iptal edilen (döngü sonrası uygulanır) for (const auto& bek : kf.bekleyenler) { const ImU32 renk = bek.yuvarlat ? IM_COL32(120, 200, 255, (int)(200 * nabiz)) : IM_COL32(255, 170, 90, (int)(200 * nabiz)); ImVec2 sb, ss, om; for (const auto& [kb2, ks2] : bek.kenarlar) if (proj(kb2, sb) && proj(ks2, ss)) dl->AddLine(sb, ss, renk, 2.2f + 1.6f * nabiz); if (proj(bek.orta, om)) { const int halkaAlfa = (int)(180.0f * (1.0f - f01)); const ImU32 halkaRenk = bek.yuvarlat ? IM_COL32(120, 200, 255, halkaAlfa) : IM_COL32(255, 170, 90, halkaAlfa); dl->AddCircle(om, 8.0f + 26.0f * f01, halkaRenk, 0, 2.0f); // büyüyüp sönen dalga halkası char b[56]; std::snprintf(b, sizeof(b), "%s %.2f mm işleniyor…", bek.yuvarlat ? "◜ R" : "◺ pah", bek.deger); gizmo::degerEtiketi(dl, om, b, IM_COL32(200, 215, 235, (int)(140 + 90 * nabiz))); // İPTAL ✕ (etiketin sağında): OCCT worker durdurulamaz ama featureIptal → async sonuç // güvenle atılır (pump'ta bul(fk)==null → sessiz atma) + ghost hemen kalkar. const ImVec2 ts = ImGui::CalcTextSize(b); const ImVec2 xm{om.x + 20.0f + ts.x + 13.0f, om.y - ts.y * 0.5f}; ImGui::SetCursorScreenPos({xm.x - 9.0f, xm.y - 9.0f}); ImGui::PushID((int)(2200u + bek.id)); const bool tikIptal = ImGui::InvisibleButton("##ghostIptal", {18, 18}); const bool hov = ImGui::IsItemHovered(); ImGui::PopID(); const ImU32 xr = hov ? IM_COL32(255, 95, 82, 255) : IM_COL32(232, 120, 108, 225); dl->AddCircleFilled(xm, 9.0f, IM_COL32(30, 20, 22, 235)); dl->AddCircle(xm, 9.0f, xr, 0, 1.6f); dl->AddLine({xm.x - 3.4f, xm.y - 3.4f}, {xm.x + 3.4f, xm.y + 3.4f}, xr, 1.8f); dl->AddLine({xm.x - 3.4f, xm.y + 3.4f}, {xm.x + 3.4f, xm.y - 3.4f}, xr, 1.8f); if (hov) ImGui::SetTooltip("İşlemi iptal et"); if (tikIptal) { iptalId = bek.id; iptalFk = bek.fk; } } } if (iptalId) { // × tıklandı → feature draft'ı sil (async sonuç atılır) + ghost'u kaldır if (iptalFk) partD.featureIptal(iptalFk); kf.bekleyenler.erase( std::remove_if(kf.bekleyenler.begin(), kf.bekleyenler.end(), [iptalId](const KenarFilletDurum::Bekleyen& b) { return b.id == iptalId; }), kf.bekleyenler.end()); ui::toastGoster("İşlem iptal edildi", ui::ToastTip::Bilgi); } } if (!kf.surukluyor) { // ---- SEÇİLİ kenarda tutamaç (hover DEĞİL) ---- if (sahne.kip() != SecimKip::Kenar) return; // yalnız KENAR seçiminde (yüz→push/pull) // Tutamaç = SON seçilen geçerli kenar (imlecin en son dokunduğu yerde belirir). u32 g = 0, a = 0; Dugum* d = nullptr; geometri::KenarBilgi kb; for (const SecimOge& o : sahne.secililer()) { Dugum* dd = sahne.bul(o.govde); geometri::KenarBilgi kb2; if (dd && dd->brep && dd->brep->kenarBilgi(o.alt, kb2)) { g = o.govde; a = o.alt; d = dd; kb = kb2; } } if (!g || !a || !d) return; // Bağlam farkındalığı: GSD açık YÜZEY / TEL kenarında fillet/pah ANLAMSIZ (katı değil) → // R/C tutamacı gösterme. Bu kenarlarda GSD gizmosu (offset/extrude) devreye girer. // İSTİSNA: GSD "Kalınlaştır" sonucu düğüm tipi Yuzey kalır ama brep KATI'dır (doluMu) → // şekilli katının kenarlarında fillet/pah GEÇERLİ, tutamaç gösterilir (kullanıcı bug'ı: // "şekilli edge'lerde pah/radüs atamıyorum"). if ((d->tip == DugumTip::Yuzey || d->tip == DugumTip::Tel) && !(d->brep && d->brep->doluMu())) return; // Oto-gizmo kapalıysa (sağ-alt toggle) R/C tutamaçları seçilen kenara ZIPLAMAZ; yalnız // Ctrl basılıyken belirir — push_pull/yuz_tasi ile aynı jest (seçim/ölçüm serbest kalır). if (!dogrudanAyar().yuzTasiOto && !dogrudanAyar().pushPullOtoOk && !ImGui::IsKeyDown(ImGuiMod_Ctrl)) return; const Vec3f orta = dunyaNok(d, kb.orta); ImVec2 sc; if (!proj(orta, sc)) return; // İKİ SİMGE-TUTAMAÇ: 'R' (radiüs/fillet) solda, 'C' (pah/chamfer) sağda. Hangisini // tıklayıp sürüklersen o işlem uygulanır (Plasticity/Fusion tarzı çoklu tutamaç). const ImVec2 filletP{sc.x - 15.0f, sc.y}; const ImVec2 pahP{sc.x + 15.0f, sc.y}; const auto yakinMi = [&](const ImVec2& p) { const f32 dx = p.x - io.MousePos.x, dy = p.y - io.MousePos.y; const f32 hr = 11.0f * dokunmaHedefCarpani(); // parmakta büyür, farede 11px return vpHover && (dx * dx + dy * dy) <= hr * hr; }; const bool yakinF = yakinMi(filletP); const bool yakinP = yakinMi(pahP) && !yakinF; // çakışmada fillet önceliği ustunde = (yakinF || yakinP) && ImGui::IsMouseDown(ImGuiMouseButton_Left); kf.tutamacYakin = (yakinF || yakinP); // dokunmatik: tuştan bağımsız yakınlık // Kenarları vurgula: TÜM seçili kenarlar soluk (hepsi feature'a girecek), tutamaç kenarı parlak ImVec2 sb, ss; for (const SecimOge& o : sahne.secililer()) { if (!o.alt || (o.govde == g && o.alt == a)) continue; Dugum* dd = sahne.bul(o.govde); geometri::KenarBilgi kb2; if (dd && dd->brep && dd->brep->kenarBilgi(o.alt, kb2) && proj(dunyaNok(dd, kb2.bas), sb) && proj(dunyaNok(dd, kb2.son), ss)) dl->AddLine(sb, ss, IM_COL32(255, 200, 110, 120), 1.8f); } if (proj(dunyaNok(d, kb.bas), sb) && proj(dunyaNok(d, kb.son), ss)) dl->AddLine(sb, ss, IM_COL32(255, 200, 110, 210), 2.4f); // Simge çizimi: pasifken koyu dolgu + renkli halka/harf; aktifken (fare yakın) renkli dolu. const auto simgeCiz = [&](const ImVec2& p, bool aktif, ImU32 renk, const char* etk) { const f32 rad = (aktif ? 11.0f : 9.0f) * dogrudanAyar().tutamacBoy; if (aktif && dogrudanAyar().glowAcik) gizmo::glowDaire(dl, p, rad, renk); // hover neon glow dl->AddCircleFilled(p, rad, aktif ? renk : IM_COL32(40, 44, 52, 235)); dl->AddCircle(p, rad, renk, 0, aktif ? 2.5f : 1.8f); const ImVec2 ts = ImGui::CalcTextSize(etk); dl->AddText({p.x - ts.x * 0.5f, p.y - ts.y * 0.5f}, aktif ? IM_COL32(20, 24, 30, 255) : renk, etk); }; simgeCiz(filletP, yakinF, IM_COL32(120, 200, 255, 255), "R"); // radiüs simgeCiz(pahP, yakinP, IM_COL32(255, 170, 90, 255), "C"); // chamfer/pah if ((yakinF || yakinP) && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { kf.surukluyor = true; kf.yuvarlat = yakinF; // R=fillet(yarıçap), C=pah(mesafe) kf.govde = g; kf.kenar = a; kf.yaricap = 0; kf.tipBuf[0] = 0; // yeni sürükleme → yazılan sayı sıfırla kf.ortaD = orta; kf.ortaEkran = sc; { // R=0'dan başlasın: tıklama anındaki ekran ofseti taban alınır (delta-sürükleme) const f32 bx = io.MousePos.x - sc.x, by = io.MousePos.y - sc.y; kf.basMesafe = std::sqrt(bx * bx + by * by); } kf.basD = dunyaNok(d, kb.bas); kf.sonD = dunyaNok(d, kb.son); // 3B önizleme için komşu iki DÜZ yüzün normalleri (eğri komşu → daire fallback). kf.komsuVar = false; const auto komsular = d->brep->kenarKomsuYuzler(a); if (komsular.size() >= 2) { geometri::YuzBilgi y1, y2; if (d->brep->yuzBilgi(komsular[0], y1) && d->brep->yuzBilgi(komsular[1], y2) && y1.duzlemMi && y2.duzlemMi) { kf.n1D = dunyaYon(d, y1.normal); kf.n2D = dunyaYon(d, y2.normal); // İç-yüz yönleri: kenar ortasından yüz CENTROID'ine, kenar teğetine dik izdüşüm. // (İşaret zorlamasız → konveks VE konkav kenarda doğru; teğet noktaları P + a·R.) const Vec3f eks = kf.sonD - kf.basD; const f32 el = std::sqrt(eks.uzunlukKare()); const Vec3f tt = el > 1e-5f ? eks * (1.0f / el) : Vec3f{1, 0, 0}; const auto icYon = [&](const Vec3d& merkezYerel) { Vec3f fc = dunyaNok(d, merkezYerel); Vec3f v = fc - kf.ortaD; v = v - tt * v.nokta(tt); // kenar teğetine dik bileşen (yüz içine) const f32 l = std::sqrt(v.uzunlukKare()); return l > 1e-5f ? v * (1.0f / l) : v; }; kf.a1D = icYon(y1.merkez); kf.a2D = icYon(y2.merkez); // İç köşe (konkav): yüz1 centroid'i yüz2'nin DIŞ tarafında (n2 yönünde) → a1·n2 > 0. kf.konkav = kf.a1D.nokta(kf.n2D) > 0.0f; kf.komsuVar = true; } } kf.eski = sahne.anlik(g); } return; } // ---- Sürükleme: fare orta noktadan uzaklaştıkça yarıçap ↑ ---- ustunde = true; const f32 dx = io.MousePos.x - kf.ortaEkran.x, dy = io.MousePos.y - kf.ortaEkran.y; const f32 ekranMesafe = std::sqrt(dx * dx + dy * dy); // Ekran-uzay sürükleme → GERÇEK yarıçap (mm): kenar orta noktasında 1mm'nin ekran-piksel // karşılığını ölçüp böl (push_pull ile aynı ilke → perspektif+ortografik otomatik doğru, // odak-mesafesinden bağımsız). Eski (ekranMesafe/vh·mesafe) fov'a bağımlı olduğu için // R'yi ~çap ölçeğinde şişiriyordu. filletHassas artık saf kullanıcı çarpanı (fizik doğru). const Vec3f kamSag = kamera.yukari().capraz(kamera.kameraYonu()).birim(); ImVec2 sMerkez, sSag; f32 pikselPerMm = 1.0f; if (proj(kf.ortaD, sMerkez) && proj(kf.ortaD + kamSag, sSag)) { const f32 px = sSag.x - sMerkez.x, py = sSag.y - sMerkez.y; pikselPerMm = std::max(std::sqrt(px * px + py * py), 1e-4f); } // DELTA sürükleme: tıklama anındaki ofset (tutamaç ortadan ~15px yanda) düşülür → // R tam 0'dan başlar, ortaya doğru sürükleyince 0'a KÜÇÜLÜR (büyüt/küçült simetrik). kf.yaricap = std::max((ekranMesafe - kf.basMesafe) / pikselPerMm * dogrudanAyar().filletHassas, 0.0f); // Sürüklerken SAYI yaz → hassas R/mesafe (drag'i override; push/pull ile aynı). Backspace sil. for (int i = 0; i < io.InputQueueCharacters.Size; ++i) { const ImWchar c = io.InputQueueCharacters[i]; const size_t l = std::strlen(kf.tipBuf); if (((c >= '0' && c <= '9') || c == '.') && l < sizeof(kf.tipBuf) - 1) { kf.tipBuf[l] = (char)c; kf.tipBuf[l + 1] = 0; } } if (ImGui::IsKeyPressed(ImGuiKey_Backspace)) { const size_t l = std::strlen(kf.tipBuf); if (l) kf.tipBuf[l - 1] = 0; } if (kf.tipBuf[0]) kf.yaricap = (f32)std::atof(kf.tipBuf); // yazılan değer drag'i geçersiz kılar const ImU32 col = IM_COL32(255, 210, 120, 245); const ImU32 colSoluk = IM_COL32(255, 210, 120, 90); if (kf.komsuVar && kf.yaricap > 1e-4f) { // GERÇEK 3B önizleme: kenar boyunca iki teğet hattı (yüzeylerde fillet sınırı) + // kesitlerde fillet yayı / pah düz kesimi. R büyüdükçe yay/kesim genişler. const Vec3f eksen = kf.sonD - kf.basD; const f32 R = kf.yaricap; const auto birimle = [](Vec3f v) { const f32 l = std::sqrt(v.uzunlukKare()); return l > 1e-5f ? v * (1.0f / l) : v; }; const Vec3f d1 = kf.a1D; // yüz1 içine (centroid'e) kenar-dik — konveks/konkav robust const Vec3f d2 = kf.a2D; // yüz2 içine std::vector h1, h2; const int L = 12; for (int i = 0; i <= L; ++i) { const Vec3f P = kf.basD + eksen * ((f32)i / L); const Vec3f P1 = P + d1 * R, P2 = P + d2 * R; ImVec2 s1, s2; const bool ok1 = proj(P1, s1), ok2 = proj(P2, s2); if (ok1) h1.push_back(s1); if (ok2) h2.push_back(s2); if (ok1 && ok2 && (i == 0 || i == L || i == L / 2)) { if (kf.yuvarlat) { // fillet yayı: merkez konveks→material içine (−n1), konkav→boşluğa (+n1) const Vec3f C = P + (d1 + (kf.konkav ? kf.n1D : kf.n1D * -1.0f)) * R; ImVec2 onc; bool var = false; const int A = 12; for (int k = 0; k <= A; ++k) { const f32 u = (f32)k / A; const Vec3f dk = birimle((P1 - C) * (1.0f - u) + (P2 - C) * u) * R; ImVec2 sc2; if (proj(C + dk, sc2)) { if (var) dl->AddLine(onc, sc2, col, 2.2f); onc = sc2; var = true; } } } else { // pah → düz kesim dl->AddLine(s1, s2, col, 2.2f); } } } for (size_t i = 1; i < h1.size(); ++i) dl->AddLine(h1[i - 1], h1[i], colSoluk, 2.0f); for (size_t i = 1; i < h2.size(); ++i) dl->AddLine(h2[i - 1], h2[i], colSoluk, 2.0f); } else { // Eğri komşu / komşu yüz yok → ekran-dairesi fallback (eski davranış). if (Dugum* d = sahne.bul(kf.govde)) { geometri::KenarBilgi kb; if (d->brep && d->brep->kenarBilgi(kf.kenar, kb)) { ImVec2 sb, ss; if (proj(dunyaNok(d, kb.bas), sb) && proj(dunyaNok(d, kb.son), ss)) dl->AddLine(sb, ss, col, 3.0f); } } // Fallback halka yarıçapı = CANLI R'nin ekran karşılığı (ham imleç uzaklığı değil) → // görsel, uygulanacak değerle birebir. if (dogrudanAyar().filletCevreGoster || dogrudanAyar().kenarFilletCevre) { const f32 R = std::max(kf.yaricap * pikselPerMm, 4.0f); dl->AddCircle(kf.ortaEkran, R, col, 0, 2.0f); // Toggle: Fillet çember önizleme if (dogrudanAyar().kenarFilletCevre) { dl->AddCircleFilled(kf.ortaEkran, 4.0f, col); // İç dolgu (toggle destekli) } } } // Yarıçap etiketi (cam pill — push/pull ile tutarlı futuristik dil) char b[48]; if (kf.tipBuf[0]) // canlı yazılan hassas değer std::snprintf(b, sizeof(b), "%s %s mm ▏", kf.yuvarlat ? "◜ R" : "◺ pah", kf.tipBuf); else std::snprintf(b, sizeof(b), "%s %.2f mm", kf.yuvarlat ? "◜ R" : "◺ pah", kf.yaricap); gizmo::degerEtiketi(dl, io.MousePos, b, kf.yuvarlat ? IM_COL32(120, 200, 255, 255) : IM_COL32(255, 170, 90, 255)); // ESC = İPTAL (işlem yapmadan çık) · Enter = ONAYLA (yazılan hassas R/pah ile commit — // fareyi bırakmadan). push_pull ile aynı jest standardı. if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { kf.surukluyor = false; kf.tipBuf[0] = 0; return; } if (ImGui::IsMouseReleased(ImGuiMouseButton_Left) || ImGui::IsKeyPressed(ImGuiKey_Enter)) { const f32 r = kf.yaricap; // yazılan sayı varsa kf.yaricap onu yansıtır (drag override) const u32 g = kf.govde, tutamacKenar = kf.kenar; kf.surukluyor = false; kf.tipBuf[0] = 0; if (r > 1e-3f) { // Bırakış → PARAMETRİK feature (R=Fillet, C=Chamfer). Seçili TÜM kenarlar GÖVDEYE göre // gruplanır → her gövdeye AYRI feature (çok gövdeli seçimde hepsi işlenir; async kuyruk // gövdeleri sırayla indirir). Seçim boşsa tutamaç kenarı. const char* tip = kf.yuvarlat ? "fillet" : "chamfer"; const char* degerKey = kf.yuvarlat ? "yaricap" : "mesafe"; std::vector>> gruplar; for (const SecimOge& o : sahne.secililer()) { if (!o.alt) continue; auto it = std::find_if(gruplar.begin(), gruplar.end(), [&](const auto& gr) { return gr.first == o.govde; }); if (it == gruplar.end()) gruplar.push_back({o.govde, {o.alt}}); else it->second.push_back(o.alt); } if (gruplar.empty()) gruplar.push_back({g, {tutamacKenar}}); const bool yuvarlatK = kf.yuvarlat; int baslayan = 0; // en az bir feature başladıysa KomutCalisti yay (makro/öğrenme) for (const auto& [gg, kenarlar] : gruplar) { const u32 fk = partD.featureBaslat(tip, VARLIK_YOK, gg); if (!fk) { ui::toastGoster(kf.yuvarlat ? "Fillet başarısız (katı gövde gerek)" : "Pah başarısız", ui::ToastTip::Hata); continue; } ++baslayan; // Parametreler TOPLU + TEK recompute (parametre başına featureGuncelle jest başına // n+2 tam zincir-recompute koşturuyordu — devasa modelde ana yavaşlık). std::vector> params; for (std::size_t i = 0; i < kenarlar.size(); ++i) params.emplace_back("kenar" + std::to_string(i), (f64)kenarlar[i]); params.emplace_back("kenarSayi", (f64)kenarlar.size()); params.emplace_back(degerKey, (f64)r); // ASYNC APPLY: OCCT hesabı worker'da — bırakışta UI donmaz, İŞ-ID'Lİ hayalet kalır // (bitti yalnız kendi kaydını siler). Desteklenmiyorsa/kapalıysa eski senkron yol. const u32 isId = ++kf.bekleyenSayac; const bool as = dogrudanAyar().asyncApply && partD.featureAsyncUygula(fk, params, [&kf, &cekirdek, &partD, yuvarlatK, isId, fk](bool ok) { kf.bekleyenler.erase( std::remove_if(kf.bekleyenler.begin(), kf.bekleyenler.end(), [isId](const KenarFilletDurum::Bekleyen& b) { return b.id == isId; }), kf.bekleyenler.end()); if (ok) dmFeatureEkleUndo(cekirdek, partD, fk); // × ile iptalde callback koşmaz ui::toastGoster(ok ? (yuvarlatK ? "Fillet feature eklendi" : "Chamfer feature eklendi") : (yuvarlatK ? "Fillet başarısız (yarıçap büyük olabilir)" : "Pah başarısız (mesafe büyük olabilir)"), ok ? ui::ToastTip::Basari : ui::ToastTip::Hata); }); if (as) { // Hayalet kaydı: bu gövdenin işlenen kenarları (dünya) bırakış ANINDA kopyalanır. KenarFilletDurum::Bekleyen bek; bek.id = isId; bek.fk = fk; // × iptal için bek.yuvarlat = kf.yuvarlat; bek.deger = r; if (Dugum* dd = sahne.bul(gg)) { geometri::KenarBilgi kb2; for (u32 ke : kenarlar) if (dd->brep && dd->brep->kenarBilgi(ke, kb2)) bek.kenarlar.emplace_back(dunyaNok(dd, kb2.bas), dunyaNok(dd, kb2.son)); } bek.orta = bek.kenarlar.empty() ? kf.ortaD : (bek.kenarlar.front().first + bek.kenarlar.front().second) * 0.5f; kf.bekleyenler.push_back(std::move(bek)); } else { partD.featureParamToplu(fk, params); if (partD.featureOnayla(fk)) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster(kf.yuvarlat ? "Fillet feature eklendi" : "Chamfer feature eklendi", ui::ToastTip::Basari); } } // Tutamaç işlemi de KomutCalisti YAYAR → makro önerisi + öğrenen menü bunu görür // (registry fillet/pah adıyla → üretilen makro doğru aracı açar). Yoksa DM'in ana // işleri (tutamaçla yapılan fillet/pah) öğrenmeye HİÇ girmezdi. if (baslayan > 0) dmTutamacOlayiYay(cekirdek, "İşlem", kf.yuvarlat ? "Kenar Yuvarlat (3B)" : "Pah (3B)"); } } } } // namespace fiko